Update ODLUX 42/123142/1
authorAijana Schumann <aijana.schumann@highstreet-technologies.com>
Thu, 5 Aug 2021 06:50:16 +0000 (08:50 +0200)
committerAijana Schumann <aijana.schumann@highstreet-technologies.com>
Thu, 5 Aug 2021 06:50:16 +0000 (08:50 +0200)
Add LineOfSightApp, update Framework, Connect, Performance and LinkCalculatorApp

Issue-ID: CCSDK-3417
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: I651a2fb771d2963aea70f916c70c8fdfd3443e87

50 files changed:
sdnr/wt/odlux/apps/app-feature/pom.xml
sdnr/wt/odlux/apps/app-installer/pom.xml
sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx
sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts
sdnr/wt/odlux/apps/lineOfSightApp/.babelrc [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/package.json [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/pom.xml [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/actions/commonActions.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/actions/mapActions.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/components/ConnectionErrorPoup.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/components/heightChart.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/components/map.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapContextMenu.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapInfo.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/config.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/handlers/mapHandler.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/handlers/rootHandler.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/hooks/d3.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/index.html [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/model/GPSProfileResult.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/model/Height.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/model/LatLon.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/pluginLineOfSight.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/services/heightService.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/styles/index.css [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/styles/mapbox-gl.css [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/utils/map.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/utils/math.ts [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src/views/main.tsx [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/src2/test/resources/test.js [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/tsconfig.json [new file with mode: 0644]
sdnr/wt/odlux/apps/lineOfSightApp/webpack.config.js [new file with mode: 0644]
sdnr/wt/odlux/apps/linkCalculationApp/src/actions/commonLinkCalculationActions.ts
sdnr/wt/odlux/apps/linkCalculationApp/src/handlers/linkCalculationAppRootHandler.ts
sdnr/wt/odlux/apps/linkCalculationApp/src/index.html
sdnr/wt/odlux/apps/linkCalculationApp/src/pluginLinkCalculation.tsx
sdnr/wt/odlux/apps/linkCalculationApp/src/views/linkCalculationComponent.tsx
sdnr/wt/odlux/apps/linkCalculationApp/webpack.config.js
sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx
sdnr/wt/odlux/framework/package.json
sdnr/wt/odlux/framework/pom.xml
sdnr/wt/odlux/framework/src/views/about.tsx
sdnr/wt/odlux/installer/pom.xml
sdnr/wt/odlux/odlux.properties
sdnr/wt/odlux/package.json
sdnr/wt/odlux/pom.xml
sdnr/wt/odlux/yarn.lock

index 738ac69..17c0dab 100644 (file)
             <artifactId>sdnr-wt-odlux-app-linkCalculationApp</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sdnr-wt-odlux-app-lineOfSightApp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
 
         
         
index 1ac8859..721d564 100755 (executable)
             <artifactId>sdnr-wt-odlux-app-linkCalculationApp</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sdnr-wt-odlux-app-lineOfSightApp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
 
     </dependencies>
 
index 5d0757a..4a7a0d2 100644 (file)
@@ -194,11 +194,12 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement
         <NetworkElementTable stickyHeader tableId="network-element-table" customActionButtons={[refreshNetworkElementsAction, ...canAdd ? [addRequireNetworkElementAction] : []]} columns={[
           { property: "nodeId", title: "Node Name", type: ColumnType.text },
           { property: "isRequired", title: "Required", type: ColumnType.boolean },
-          { property: "status", title: "Connection Status", type: ColumnType.text },
+          { property: "status", title: "Connection Status", type: ColumnType.text, width:'15%' },
           { property: "host", title: "Host", type: ColumnType.text },
           { property: "port", title: "Port", type: ColumnType.numeric },
           { property: "coreModelCapability", title: "Core Model", type: ColumnType.text },
-          { property: "deviceType", title: "Type", type: ColumnType.text },
+          { property: "deviceType", title: "Device Type", type: ColumnType.text },
+          { property: "deviceFunction", title: "Device Function", type: ColumnType.text, width: '15%' }
         ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus createContextMenu={rowData => {
 
           return this.getContextMenu(rowData);
index 89070ab..bc15afb 100644 (file)
@@ -30,6 +30,7 @@ export type NetworkElementConnection = {
   status?: "Connected" | "mounted" | "unmounted" | "Connecting" | "Disconnected" | "idle";
   coreModelCapability?: string;
   deviceType?: string;
+  deviceFunction?: string;
   nodeDetails?: {
     availableCapabilites: {
       capabilityOrigin: string;
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/.babelrc b/sdnr/wt/odlux/apps/lineOfSightApp/.babelrc
new file mode 100644 (file)
index 0000000..3d8cd12
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "presets": [
+    ["@babel/preset-react"],
+    ["@babel/preset-env", {
+      "targets": {
+        "chrome": "66"
+      },
+      "spec": true,
+      "loose": false,
+      "modules": false,
+      "debug": false,
+      "useBuiltIns": "usage",
+      "forceAllTransforms": true
+    }]
+  ],
+  "plugins": []
+}
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/package.json b/sdnr/wt/odlux/apps/lineOfSightApp/package.json
new file mode 100644 (file)
index 0000000..dbba7ec
--- /dev/null
@@ -0,0 +1,47 @@
+{
+  "name": "@odlux/line-of-sight-app",
+  "version": "0.1.0",
+  "description": "A react based modular UI to display event log from a database.",
+  "main": "index.js",
+  "scripts": {
+    "start": "webpack-dev-server --env debug",
+    "build": "webpack --env release --config webpack.config.js",
+    "build:dev": "webpack --env debug --config webpack.config.js"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://git.mfico.de/highstreet-technologies/odlux.git"
+  },
+  "keywords": [
+    "reactjs",
+    "redux",
+    "ui",
+    "framework"
+  ],
+  "author": "Aijana Schumann",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "@odlux/framework": "*",
+    "@types/d3": "^6.7.0",
+    "@types/mapbox-gl": "^1.10.2",
+    "@types/node": "^12.0.0",
+    "d3": "^7.0.0",
+    "d3-polygon": "^3.0.1",
+    "mapbox-gl": "^1.11.0",
+    "object.values": "^1.1.1"
+  },
+  "peerDependencies": {
+    "@material-ui/core": "4.11.4",
+    "@material-ui/icons": "4.11.2",
+    "@types/classnames": "2.2.6",
+    "@types/flux": "3.1.8",
+    "@types/jquery": "3.3.10",
+    "@types/react": "17.0.3",
+    "@types/react-dom": "17.0.2",
+    "@types/react-router-dom": "5.1.7",
+    "jquery": "3.3.1",
+    "react": "17.0.1",
+    "react-dom": "17.0.1",
+    "react-router-dom": "5.2.0"
+  }
+}
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/pom.xml b/sdnr/wt/odlux/apps/lineOfSightApp/pom.xml
new file mode 100644 (file)
index 0000000..29184a7
--- /dev/null
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~ ONAP : ccsdk features
+  ~ ================================================================================
+  ~ Copyright (C) 2020 AT&T 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=======================================================
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onap.ccsdk.parent</groupId>
+        <artifactId>binding-parent</artifactId>
+        <version>2.2.0-SNAPSHOT</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.onap.ccsdk.features.sdnr.wt</groupId>
+    <artifactId>sdnr-wt-odlux-app-lineOfSightApp</artifactId>
+    <version>1.2.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>ccsdk-features :: ${project.artifactId}</name>
+    <licenses>
+        <license>
+            <name>Apache License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0</url>
+        </license>
+    </licenses>
+
+    <properties>
+        <maven.javadoc.skip>true</maven.javadoc.skip>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sdnr-wt-odlux-core-model</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sdnr-wt-odlux-core-provider</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <sourceDirectory>src2/main/java</sourceDirectory>
+        <resources>
+            <resource>
+                <directory>dist</directory>
+                <targetPath>odlux</targetPath>
+            </resource>
+            <resource>
+                <directory>src2/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>src2/test/resources</directory>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <artifactId>maven-clean-plugin</artifactId>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <directory>dist</directory>
+                            <followSymlinks>false</followSymlinks>
+                        </fileset>
+                        <fileset>
+                            <directory>node</directory>
+                            <followSymlinks>false</followSymlinks>
+                        </fileset>
+                        <fileset>
+                            <directory>node_modules</directory>
+                            <followSymlinks>false</followSymlinks>
+                        </fileset>
+                        <fileset>
+                            <directory>../node_modules</directory>
+                            <followSymlinks>false</followSymlinks>
+                        </fileset>
+                        <!-- eclipse bug build bin folder in basedir -->
+                        <fileset>
+                            <directory>bin</directory>
+                            <followSymlinks>false</followSymlinks>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-test-source</id>
+                        <phase>generate-test-sources</phase>
+                        <goals>
+                            <goal>add-test-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>src2/test/java</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>de.jacks-it-lab</groupId>
+                <artifactId>frontend-maven-plugin</artifactId>
+                <version>1.7.2</version>
+                <executions>
+                    <execution>
+                        <id>install node and yarn</id>
+                        <goals>
+                            <goal>install-node-and-yarn</goal>
+                        </goals>
+                        <!-- optional: default phase is "generate-resources" -->
+                        <phase>initialize</phase>
+                        <configuration>
+                            <nodeVersion>v12.13.0</nodeVersion>
+                            <yarnVersion>v1.22.10</yarnVersion>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>yarn build</id>
+                        <goals>
+                            <goal>yarn</goal>
+                        </goals>
+                        <configuration>
+                            <arguments>run build</arguments>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/actions/commonActions.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/actions/commonActions.ts
new file mode 100644 (file)
index 0000000..3cc8ea4
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * ============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 { Height } from "model/Height";
+import { isNumber } from "../utils/math";
+import { Action } from "../../../../framework/src/flux/action";
+ import { Dispatch } from "../../../../framework/src/flux/store";
+ import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
+ import { LatLon } from "../model/LatLon";
+
+ export class SetPassedInValuesAction extends Action{
+     constructor(public start: LatLon, public end: LatLon, public center: LatLon, public heightA : Height |null, public heightB: Height |null){
+         super();
+     }
+ }
+
+ export class SetReachableAction extends Action{
+     constructor(public reachable: boolean | null){
+         super();
+     }
+ }
+
+ export const SetPassedInValues = (values: (string|null)[]) => (dispatcher: Dispatch) =>{
+
+     const start: LatLon = {latitude: Number(values[0]), longitude: Number(values[1])}
+     const end: LatLon = {latitude: Number(values[2]), longitude: Number(values[3])};
+     const midpoint = calculateMidPoint(start.latitude, start.longitude, end.latitude, end.longitude);
+     const center: LatLon = {latitude: midpoint[1], longitude: midpoint[0]};
+     const heightA: Height | null = isNumber(values[4]) && isNumber(values[5]) ? {amsl:+values[4]!, antennaHeight: +values[5]!} : null;
+     const heightB: Height | null = isNumber(values[6]) && isNumber(values[7]) ? {amsl:+values[6]!, antennaHeight: +values[7]!} : null;
+
+
+    dispatcher(new SetPassedInValuesAction(start, end, center, heightA, heightB));
+ }
+
+ //taken from https://www.movable-type.co.uk/scripts/latlong.html
+const calculateMidPoint = (lat1: number, lon1: number, lat2: number, lon2: number) =>{
+
+    const dLon = degrees_to_radians(lon2 - lon1);
+
+    //convert to radians
+    lat1 = degrees_to_radians(lat1);
+    lat2 = degrees_to_radians(lat2);
+    lon1 = degrees_to_radians(lon1);
+
+    const Bx = Math.cos(lat2) * Math.cos(dLon);
+    const By = Math.cos(lat2) * Math.sin(dLon);
+    const lat3 = Math.atan2(Math.sin(lat1) + Math.sin(lat2), Math.sqrt((Math.cos(lat1) + Bx) * (Math.cos(lat1) + Bx) + By * By));
+    const lon3 = lon1 + Math.atan2(By, Math.cos(lat1) + Bx);
+
+    return [radians_to_degrees(lon3), radians_to_degrees(lat3)];
+}
+
+const degrees_to_radians = (degrees: number) =>
+{
+return degrees * (Math.PI/180);
+}
+
+const radians_to_degrees = (radians:number) =>{
+    
+    var pi = Math.PI;
+    return radians * (180/pi);
+}
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/actions/mapActions.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/actions/mapActions.ts
new file mode 100644 (file)
index 0000000..37ef5ee
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * ============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 { LatLon } from "../model/LatLon";
+import { Action } from "../../../../framework/src/flux/action";
+import { Height } from "model/Height";
+
+export class SetChartAction extends Action{
+    constructor(public startPoint: LatLon, public endPoint: LatLon, public heightA: Height, public heightB: Height){
+        super();
+    }
+}
+
+export class SetStartPointAction extends Action{
+    constructor(public startPoint: LatLon|null){
+        super();
+    }
+}
+
+export class SetEndpointAction extends Action{
+    constructor(public endPoint: LatLon|null){
+        super();
+    }
+}
+
+export class SetHeightA extends Action{
+    constructor(public height: Height){
+        super();
+    }
+}
+
+export class SetHeightB extends Action{
+    constructor(public height: Height){
+        super();
+    }
+}
+
+export class ClearSavedChartAction extends Action{
+    constructor(){
+        super();
+    }
+}
+
+export class SetMapCenterAction extends Action{
+    /**
+     *
+     */
+    constructor(public point: LatLon, public zoom: number) {
+        super();
+        
+    }
+}
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/ConnectionErrorPoup.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/components/ConnectionErrorPoup.tsx
new file mode 100644 (file)
index 0000000..7d9339f
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * ============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> = (props) => {
+
+    return (props.reachable === false ?  
+    <Paper style={{padding:5, position: 'absolute', top: 160, width: 230, left:"40%",  zIndex:1}}>
+        <div style={{display: 'flex', flexDirection: 'column'}}>
+        <div style={{'alignSelf': 'center', marginBottom:5}}> <Typography> <FontAwesomeIcon icon={faExclamationTriangle} /> Connection Error</Typography></div>
+         <Typography>Service unavailable</Typography>
+        </div>
+    </Paper> : 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
new file mode 100644 (file)
index 0000000..3030fe7
--- /dev/null
@@ -0,0 +1,126 @@
+/**
+ * ============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<HeightMapProps> = (props) => {
+  const { data, dataMin, dataMax, heightPosA, heightPosB } = props;
+  let ref: React.RefObject<SVGSVGElement>
+
+  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 (
+    <svg ref={ref!} width={props.width} height={props.height} />
+
+  );
+}
+
+export { HeightChart };
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/map.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/components/map.tsx
new file mode 100644 (file)
index 0000000..6f29d59
--- /dev/null
@@ -0,0 +1,329 @@
+/**
+ * ============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<typeof mapStateToProps, typeof mapDispatchToProps>;
+
+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<mapProps> = (props) => {
+
+  //const [start, setStart] = React.useState<mapboxgl.LngLat| undefined>();
+  //const [end, setEnd] = React.useState<mapboxgl.LngLat| undefined>();
+  const [data, setData] = React.useState<GPSProfileResult[] | number>(Number.NaN);
+  const [dataMin, setDataMin] = React.useState<GPSProfileResult|undefined>();
+  const [dataMax, setDataMax] = React.useState<GPSProfileResult|undefined>();
+  const [isMapLoaded, setMapLoaded] = React.useState<boolean>(false);
+
+
+const mapRef = React.useRef<{ map: mapboxgl.Map | null }>({ map: null });
+const mapContainerRef = React.useRef<HTMLDivElement>(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(
+                  <MapContextMenu pos={e.lngLat}
+                    onStart={(p) => { 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 <>
+    <div id="map" style={{ width: "100%", height:'100%', position: 'relative' }} ref={mapContainerRef} >
+    <MapInfo minHeight={dataMin} maxHeight={dataMax}  />
+    <ConnectionErrorPoup reachable={props.ready} />
+    
+    {typeof data === "object"
+        ? (
+          < div className={classes.chart} onClick={() => {
+            setData(Number.NaN);
+            setDataMax(undefined);
+            setDataMin(undefined);
+            props.ClearChartAction();
+          }}>
+            <HeightChart heightPosA={heightA} heightPosB={heightB} width={mapContainerRef.current?.clientWidth!} height={mapContainerRef.current?.clientHeight!} data={data} dataMin={dataMin!} dataMax={dataMax!} />
+          </div>
+        )
+        : null
+      }
+
+        </div>
+        </>
+}
+
+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
new file mode 100644 (file)
index 0000000..0fc51ca
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * ============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<MapContextMenuProps> = (props) => {
+    const { pos, onStart, onEnd } = props;
+    const [height, setHeight] = useState<number | undefined>(undefined);
+    const [value1, setValue1] = useState<string>('');
+    const [value2, setValue2] = useState<string>('');
+
+    const classes = styles();
+  
+    useEffect(() => {
+      getGPSHeight({ longitude: pos.lng, latitude: pos.lat }).then(setHeight);
+    }, [pos.lat, pos.lng]);
+
+    const handleChangeHeight = (e:React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>, id: "heightA"|"heightB") =>{
+      
+      //sanitize non numbers
+      const onlyNums = e.target.value.replace(/[^0-9]/g, '');
+      
+      if(id==="heightA"){
+        setValue1(onlyNums);
+      }else{
+        setValue2(onlyNums);
+      }
+    }
+  
+    return (
+      <div>
+        <div>Height: {height} m</div>
+        <div>
+        <div className={classes.flexContainer}>
+          <Button className={classes.button} variant="contained" onClick={() => { onStart(pos); props.onHeightA(height!,+value1); }}>Start</Button>
+          <Tooltip title="Please add the antenna height in meters above sea level.">
+          <TextField className={classes.textField} value={value1} onChange={(e)=>handleChangeHeight(e,"heightA")} InputProps={{endAdornment: <InputAdornment position="start">m</InputAdornment>}}/>
+          </Tooltip>
+          </div>
+        <div className={classes.flexContainer}>
+          <Button className={classes.button} variant="contained" onClick={() => { onEnd(pos); props.onHeightB(height!,+value2);}}>End</Button>
+          <Tooltip title="Please add the antenna height in meters above sea level.">
+          <TextField className={classes.textField} value={value2} onChange={(e)=>handleChangeHeight(e,"heightB")} InputProps={{endAdornment: <InputAdornment position="start">m</InputAdornment>}}/>
+          </Tooltip>
+          </div>
+        </div>
+        
+      </div>
+    );
+  };
+  
+
+  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
new file mode 100644 (file)
index 0000000..43a6e47
--- /dev/null
@@ -0,0 +1,171 @@
+/**
+ * ============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<typeof mapStateToProps, typeof mapDispatchToProps> & {
+    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> = (props) =>{
+
+    const [expanded, setExpanded] = React.useState(false);
+    const [width, setWidth] = React.useState(470);
+    const [length, setLength] = React.useState<string | undefined>();
+
+    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 <Accordion className={classes.accordion} expanded={expanded} onChange={handleChange}>
+    <AccordionSummary
+      expandIcon={<ExpandMoreIcon />}
+      aria-controls="panel1a-content"
+      id="panel1a-header"
+    >
+      <Typography >Map Info</Typography>
+    </AccordionSummary>
+    <AccordionDetails className={classes.container}>
+
+    
+    <Typography style={{ fontWeight: "bold", flex: "1" }} >Map Center</Typography>
+    
+    <div >
+        <div style={{ display: 'flex', flexDirection: "row" }}> 
+        <Typography className={classes.caption}> Longitude</Typography><Typography>{center.longitude}</Typography></div>
+        <div style={{ display: 'flex', flexDirection: "row" }}> 
+        <Typography className={classes.caption}> Latitude</Typography><Typography>{center.latitude}</Typography></div>
+        <div style={{ display: 'flex', flexDirection: "row" }}> 
+        <Typography className={classes.caption}> Zoom</Typography><Typography> {zoom}</Typography></div>
+        
+    </div>
+    <Typography style={{ fontWeight: "bold", flex: "1", marginTop:5 }} >Link</Typography>
+    
+    <div>
+    <div style={{ display: 'flex', flexDirection: "row", marginLeft:"38%" }}> 
+    <Typography className={classes.titleRowElement}> Start</Typography>
+    <Typography className={classes.titleRowElement}> End</Typography>
+    </div>
+
+    <div style={{ display: 'flex', flexDirection: "row" }}> 
+    
+    <Typography className={classes.caption}> Longitude</Typography>
+    <Typography className={classes.secondRow}> {start?.longitude.toFixed(3)}</Typography>
+    <Typography className={classes.secondRow}> {end?.longitude.toFixed(3)}</Typography></div>
+
+    
+    <div style={{ display: 'flex', flexDirection: "row" }}> 
+    
+    <Typography className={classes.caption}> Latitude</Typography>
+    <Typography className={classes.secondRow}> {start?.latitude.toFixed(3)}</Typography>
+    <Typography className={classes.secondRow}> {end?.latitude.toFixed(3)}</Typography></div>
+
+    
+
+    <div style={{ display: 'flex', flexDirection: "row" }}> 
+    
+    <Typography className={classes.caption}> Meassured height [m]</Typography>
+    <Typography className={classes.secondRow}> {heightA?.amsl}</Typography>
+    <Typography className={classes.secondRow}> {heightB?.amsl}</Typography>
+    </div>
+
+    <div style={{ display: 'flex', flexDirection: "row" }}> 
+    
+    <Typography className={classes.caption}> Antenna height [m] </Typography>
+    <Typography className={classes.secondRow}> {heightA?.antennaHeight}</Typography>
+    <Typography className={classes.secondRow}> {heightB?.antennaHeight}</Typography>
+    </div>
+
+    <div style={{ display: 'flex', flexDirection: "row" }}> 
+    
+    <Typography className={classes.caption}> Length [m]</Typography>
+    <Typography className={classes.secondRow}> {length}</Typography>
+  
+    </div>
+
+    <div style={{ display: 'flex', flexDirection: "row" }}> 
+    
+    <Typography className={classes.caption}> Max height @ position </Typography>
+    <Typography className={classes.thirdRow}> {maxHeight? maxHeight.height+' m': ''}</Typography>
+    <Typography className={classes.thirdRow}> {maxHeight?.gps.longitude.toFixed(3)}</Typography>
+    <Typography className={classes.thirdRow}> {maxHeight?.gps.latitude.toFixed(3)}</Typography>
+    </div>
+
+    <div style={{ display: 'flex', flexDirection: "row" }}> 
+    
+    <Typography className={classes.caption}> Min height @ position</Typography>
+    <Typography className={classes.thirdRow}> {minHeight? minHeight.height +' m': ''}</Typography>
+    <Typography className={classes.thirdRow}> {minHeight?.gps.longitude.toFixed(3)}</Typography>
+    <Typography className={classes.thirdRow}> {minHeight?.gps.latitude.toFixed(3)}</Typography>
+    </div>
+
+    </div>
+</AccordionDetails>
+</Accordion>
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(MapInfo);
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/config.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/config.ts
new file mode 100644 (file)
index 0000000..bc1e1ff
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * ============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 const URL_BASEPATH="lineOfSight";
+
+export const TERRAIN_URL="/terrain"; //http://10.20.11.163:5200  /terrain
+
+export const TILE_URL="/tiles"; //http://tile.openstreetmap.org  /tiles
+
+
+export const OSM_STYLE = {
+    'version': 8,
+    'sources': {
+        'raster-tiles': {
+            'type': 'raster',
+            'tiles': [
+                TILE_URL+'/{z}/{x}/{y}.png'
+            ],
+            'tileSize': 256,
+            'attribution':
+                '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
+        }
+    },
+    'layers': [
+        {
+            'id': 'simple-tiles',
+            'type': 'raster',
+            'source': 'raster-tiles',
+            'minZoom': 0,
+            'maxZoom': 18
+        }
+    ]
+};
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/handlers/mapHandler.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/handlers/mapHandler.ts
new file mode 100644 (file)
index 0000000..6d11977
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * ============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 { LatLon } from "../model/LatLon";
+import { IActionHandler } from "../../../../framework/src/flux/action";
+import { SetPassedInValuesAction, SetReachableAction } from "../actions/commonActions";
+import { ClearSavedChartAction, SetChartAction, SetEndpointAction, SetHeightA, SetHeightB, SetMapCenterAction, SetStartPointAction } from "../actions/mapActions";
+import { Height } from "model/Height";
+import { isNullOrUndefined } from "util";
+
+ export interface IMap {
+   center: LatLon;
+   zoom: number;
+   start: LatLon |null;
+   heightA: Height | null;
+   end: LatLon|null;
+   heightB: Height | null;
+   ready: boolean |null;
+ }
+ const initialState: IMap = {
+    center: {latitude:52.4003, longitude:13.0584},
+    zoom: 12,
+    start: null,
+    end: null,
+    ready: null,
+    heightA: null,
+    heightB: null
+
+ }
+ export const mapHandler: IActionHandler<IMap> = (state = initialState, action) => {
+   if (action instanceof SetPassedInValuesAction) {
+     state = { ...state, start: action.start, end: action.end, center: action.center, heightA: action.heightA, heightB: action.heightB };
+   }
+   else if(action instanceof SetReachableAction){
+    state = { ...state, ready: action.reachable };
+     
+   }else if(action instanceof SetChartAction){
+     state = {...state, start:action.startPoint, end: action.endPoint, heightA: action.heightA, heightB: action.heightB}
+   }
+   else if(action instanceof SetStartPointAction){
+    state = {...state, start:action.startPoint}
+
+   }
+   else if(action instanceof SetEndpointAction){
+    state = {...state, end:action.endPoint}
+
+   }
+   else if(action instanceof SetHeightA){
+    state = {...state, heightA:action.height}
+
+   }
+   else if(action instanceof SetHeightB){
+    state = {...state, heightB:action.height}
+
+   }
+   else if(action instanceof ClearSavedChartAction){
+     state= {...state, start: null, end: null, heightA:null, heightB: null}
+   }else if(action instanceof SetMapCenterAction){
+     state={...state, zoom: action.zoom,center:action.point}
+   }
+   return state;
+ }
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/handlers/rootHandler.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/handlers/rootHandler.ts
new file mode 100644 (file)
index 0000000..e7d58c4
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+* ============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==========================================================================
+*/
+// main state handler
+
+import { combineActionHandler } from '../../../../framework/src/flux/middleware';
+
+// ** do not remove **
+import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
+import { IActionHandler } from '../../../../framework/src/flux/action';
+
+
+import { IMap, mapHandler } from './mapHandler';
+
+export interface ILineOfSightAppStateState {
+  map: IMap;
+}
+
+
+
+
+declare module '../../../../framework/src/store/applicationStore' {
+  interface IApplicationStoreState {
+    lineOfSight: ILineOfSightAppStateState;
+  }
+}
+
+const actionHandlers = {
+  map: mapHandler,
+};
+
+export const lineofSightRootHandler = combineActionHandler<ILineOfSightAppStateState>(actionHandlers);
+export default lineofSightRootHandler;
+
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/hooks/d3.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/hooks/d3.ts
new file mode 100644 (file)
index 0000000..dfebe86
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * ============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==========================================================================
+ */
+
+/* eslint-disable react-hooks/exhaustive-deps */
+import { useEffect, useRef } from 'react';
+import type { DependencyList } from 'react';
+
+import * as d3 from 'd3';
+
+
+type SelectionType =  d3.Selection<SVGSVGElement, d3.BaseType, null, undefined>;
+
+export const useD3 = (renderChartFn: (selection: SelectionType) => void, dependencies: DependencyList) => {
+  const ref = useRef<SVGSVGElement>(null);
+
+  useEffect(() => {
+    if (ref.current) renderChartFn(d3.select(ref.current)); 
+    return () => { };
+  }, dependencies); 
+  
+  return ref;
+}
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/index.html b/sdnr/wt/odlux/apps/lineOfSightApp/src/index.html
new file mode 100644 (file)
index 0000000..6c1478e
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <meta http-equiv="X-UA-Compatible" content="ie=edge">
+  <!-- <link rel="stylesheet" href="./vendor.css" > -->
+  <title>LineOfSightApp</title>
+</head>
+
+<body>
+  <div id="app"></div>
+  <script type="text/javascript" src="./require.js"></script>
+  <script type="text/javascript" src="./config.js"></script>
+  
+  <script>
+    // run the application
+    require(["app","connectApp","faultApp", "networkMapApp", "lineOfSightApp", "linkCalculationApp"], function (app, connectApp, faultApp, networkMapApp, lineOfSightApp, linkCalculationApp) {
+      connectApp.register();
+      faultApp.register();
+      //configurationApp.register();
+      //linkCalculationApp.register();
+      networkMapApp.register();
+      lineOfSightApp.register();
+      app("./app.tsx").runApplication();
+    });
+  </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/model/GPSProfileResult.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/model/GPSProfileResult.ts
new file mode 100644 (file)
index 0000000..567946b
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * ============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 GPSProfileResult = { height: number, gps: { latitude: number, longitude: number }, band: string, zone: number, easting: number, northing: number };
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/model/Height.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/model/Height.tsx
new file mode 100644 (file)
index 0000000..7014095
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * ============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 Height = {
+    amsl: number;
+    antennaHeight: number;
+}
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/model/LatLon.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/model/LatLon.ts
new file mode 100644 (file)
index 0000000..a447aa5
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * ============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 LatLon ={
+    latitude: number,
+    longitude: number
+}
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/pluginLineOfSight.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/pluginLineOfSight.tsx
new file mode 100644 (file)
index 0000000..b193cfb
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * ============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==========================================================================
+ */
+
+// app configuration and main entry point for the app
+
+import * as React from "react";
+import { faRoute } from '@fortawesome/free-solid-svg-icons'; // select app icon
+import applicationManager from '../../../framework/src/services/applicationManager';
+
+
+import { lineofSightRootHandler } from './handlers/rootHandler';
+import MainView from "./views/main";
+import applicationApi from "../../../framework/src/services/applicationApi";
+
+import { Redirect, Route, RouteComponentProps, Switch, useLocation, withRouter } from "react-router-dom";
+import connect, { Connect, IDispatcher } from "../../../framework/src/flux/connect";
+import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
+import { SetPassedInValues, SetReachableAction } from "./actions/commonActions";
+import { TERRAIN_URL, TILE_URL } from "./config";
+import { isNumber } from "./utils/math";
+
+const mapProps = (state: IApplicationStoreState) => ({
+});
+
+const mapDisp = (dispatcher: IDispatcher) => ({
+  setPassedInValues: (values: (string | null)[]) => dispatcher.dispatch(SetPassedInValues(values)),
+  setReachable: (reachable: boolean) => dispatcher.dispatch(new SetReachableAction(reachable))
+
+});
+
+let lastSearch = "";
+
+const useQuery = () => {
+  return new URLSearchParams(useLocation().search);
+}
+
+
+const LineOfSightApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDisp>) => {
+
+  let query = useQuery();
+
+  // called when component finshed mounting
+  React.useEffect(() => {
+    extractAndDispatchUrlValues(props.location.search);
+
+    //check tiles/terrain connectivity
+    tryCheckConnection();
+
+  }, []);
+
+
+  const extractAndDispatchUrlValues = (url: string) => {
+
+    if (lastSearch !== url) {
+      lastSearch = url;
+
+      //if mandatory values aren't there, do nothing
+      if (areMandatoryParamsPresent(query)) {
+        const values = extractValuesFromURL(query);
+        props.setPassedInValues(values);
+      }
+    }
+  }
+
+  const tryCheckConnection =() =>{
+    const terrain = fetch(`${TERRAIN_URL}/`);
+    const tiles = fetch(`${TILE_URL}/10/0/0.png`);
+
+    Promise.all([terrain, tiles])
+      .then((result) => {
+        props.setReachable(true);
+
+    })
+    .catch(error=>{
+      console.error("services not reachable.");
+      console.error(error);
+      props.setReachable(false);
+
+      })
+
+  }
+
+  /***
+   * 
+   * Checks if lat1, lon1, lat2, lon2 were passed in as url parameters
+   */
+  const areMandatoryParamsPresent = (query: URLSearchParams) => {
+
+    return isNumber(query.get("lat1")) && isNumber(query.get("lon1")) && isNumber(query.get("lat2")) && isNumber(query.get("lon2"))
+
+  }
+
+  const extractValuesFromURL = (query: URLSearchParams) => { 
+
+    return [query.get("lat1"), query.get("lon1"), query.get("lat2"), query.get("lon2"), query.get("amslA"), query.get("antennaHeightA"), query.get("amslB"), query.get("antennaHeightB")]
+  }
+
+  return (
+    <MainView />
+  );
+});
+
+
+const LoSRouterApp = withRouter(connect(mapProps, mapDisp)((props: RouteComponentProps & Connect<typeof mapProps, typeof mapDisp>) => {
+
+  return (
+    <Switch>
+      <Route path={`${props.match.path}`} component={LineOfSightApplicationRouteAdapter} />
+      <Redirect to={`${props.match.path}`} />
+    </Switch>
+  )
+}));
+
+export function register() {
+  applicationManager.registerApplication({
+    name: "lineOfSight", // used as name of state as well
+    icon: faRoute,
+    rootActionHandler: lineofSightRootHandler,
+    rootComponent: LoSRouterApp,
+    menuEntry: "Line of Sight"
+  });
+}
+
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/services/heightService.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/services/heightService.ts
new file mode 100644 (file)
index 0000000..8b05358
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * ============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 { GPSProfileResult } from "../model/GPSProfileResult";
+import { TERRAIN_URL } from "../config";
+import { LatLon } from "../model/LatLon";
+
+export const apiUrlBase="api/Query";
+
+export const getGPSProfile = async (start: LatLon, end: LatLon) => {
+  const url = `${TERRAIN_URL}/${apiUrlBase}/GPSProfileRecords`;
+
+  const result = await fetch(url, {
+    method: "POST",
+    body: JSON.stringify({ start, end }),
+    headers: {
+      'Content-Type': 'application/json'
+      // 'Content-Type': 'application/x-www-form-urlencoded',
+    },
+  });
+  if (result.ok) {
+    const data = await result.json() as GPSProfileResult[];
+    return data;
+  }
+
+  return Number.NaN;
+}
+
+export const getGPSHeight = async (gpsCoord: LatLon) => {
+  const url = `${TERRAIN_URL}/${apiUrlBase}/GPSHeight`;
+
+  const result = await fetch(url, {
+    method: "POST",
+    body: JSON.stringify(gpsCoord),
+    headers: {
+      'Content-Type': 'application/json'
+      // 'Content-Type': 'application/x-www-form-urlencoded',
+    },
+  });
+  if (result.ok) {
+    const data = await result.json() as { height: number };
+    return data.height;
+  }else{
+    return undefined;
+  }
+}
+
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/styles/index.css b/sdnr/wt/odlux/apps/lineOfSightApp/src/styles/index.css
new file mode 100644 (file)
index 0000000..ec2585e
--- /dev/null
@@ -0,0 +1,13 @@
+body {
+  margin: 0;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+    sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+    monospace;
+}
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/styles/mapbox-gl.css b/sdnr/wt/odlux/apps/lineOfSightApp/src/styles/mapbox-gl.css
new file mode 100644 (file)
index 0000000..03c479a
--- /dev/null
@@ -0,0 +1 @@
+.mapboxgl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgba(0,0,0,0);text-align:left}.mapboxgl-map:-webkit-full-screen{width:100%;height:100%}.mapboxgl-canary{background-color:salmon}.mapboxgl-canvas-container.mapboxgl-interactive,.mapboxgl-ctrl-group button.mapboxgl-ctrl-compass{cursor:-webkit-grab;cursor:-moz-grab;cursor:grab;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.mapboxgl-canvas-container.mapboxgl-interactive.mapboxgl-track-pointer{cursor:pointer}.mapboxgl-canvas-container.mapboxgl-interactive:active,.mapboxgl-ctrl-group button.mapboxgl-ctrl-compass:active{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate,.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate .mapboxgl-canvas{touch-action:pan-x pan-y}.mapboxgl-canvas-container.mapboxgl-touch-drag-pan,.mapboxgl-canvas-container.mapboxgl-touch-drag-pan .mapboxgl-canvas{touch-action:pinch-zoom}.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate.mapboxgl-touch-drag-pan,.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate.mapboxgl-touch-drag-pan .mapboxgl-canvas{touch-action:none}.mapboxgl-ctrl-bottom-left,.mapboxgl-ctrl-bottom-right,.mapboxgl-ctrl-top-left,.mapboxgl-ctrl-top-right{position:absolute;pointer-events:none;z-index:2}.mapboxgl-ctrl-top-left{top:0;left:0}.mapboxgl-ctrl-top-right{top:0;right:0}.mapboxgl-ctrl-bottom-left{bottom:0;left:0}.mapboxgl-ctrl-bottom-right{right:0;bottom:0}.mapboxgl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.mapboxgl-ctrl-top-left .mapboxgl-ctrl{margin:10px 0 0 10px;float:left}.mapboxgl-ctrl-top-right .mapboxgl-ctrl{margin:10px 10px 0 0;float:right}.mapboxgl-ctrl-bottom-left .mapboxgl-ctrl{margin:0 0 10px 10px;float:left}.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl{margin:0 10px 10px 0;float:right}.mapboxgl-ctrl-group{border-radius:4px;background:#fff}.mapboxgl-ctrl-group:not(:empty){-moz-box-shadow:0 0 2px rgba(0,0,0,.1);-webkit-box-shadow:0 0 2px rgba(0,0,0,.1);box-shadow:0 0 0 2px rgba(0,0,0,.1)}@media (-ms-high-contrast:active){.mapboxgl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.mapboxgl-ctrl-group button{width:29px;height:29px;display:block;padding:0;outline:none;border:0;box-sizing:border-box;background-color:transparent;cursor:pointer}.mapboxgl-ctrl-group button+button{border-top:1px solid #ddd}.mapboxgl-ctrl button .mapboxgl-ctrl-icon{display:block;width:100%;height:100%;background-repeat:no-repeat;background-position:50%}@media (-ms-high-contrast:active){.mapboxgl-ctrl-icon{background-color:transparent}.mapboxgl-ctrl-group button+button{border-top:1px solid ButtonText}}.mapboxgl-ctrl button::-moz-focus-inner{border:0;padding:0}.mapboxgl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.mapboxgl-ctrl button:disabled{cursor:not-allowed}.mapboxgl-ctrl button:disabled .mapboxgl-ctrl-icon{opacity:.25}.mapboxgl-ctrl button:not(:disabled):hover{background-color:rgba(0,0,0,.05)}.mapboxgl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.mapboxgl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.mapboxgl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.mapboxgl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.mapboxgl-ctrl-group button:focus:only-child{border-radius:inherit}.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E")}@media (-ms-high-contrast:active){.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E")}}@media (-ms-high-contrast:black-on-white){.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E")}}.mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E")}@media (-ms-high-contrast:active){.mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E")}}@media (-ms-high-contrast:black-on-white){.mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E")}}.mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E")}@media (-ms-high-contrast:active){.mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23999'/%3E%3C/svg%3E")}}@media (-ms-high-contrast:black-on-white){.mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E")}}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23aaa'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath d='M14 5l1 1-9 9-1-1 9-9z' fill='red'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active-error .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e58978'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background-error .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e54e33'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-waiting .mapboxgl-ctrl-icon{-webkit-animation:mapboxgl-spin 2s linear infinite;-moz-animation:mapboxgl-spin 2s infinite linear;-o-animation:mapboxgl-spin 2s infinite linear;-ms-animation:mapboxgl-spin 2s infinite linear;animation:mapboxgl-spin 2s linear infinite}@media (-ms-high-contrast:active){.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23999'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath d='M14 5l1 1-9 9-1-1 9-9z' fill='red'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active-error .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e58978'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background-error .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e54e33'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E")}}@media (-ms-high-contrast:black-on-white){.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23666'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath d='M14 5l1 1-9 9-1-1 9-9z' fill='red'/%3E%3C/svg%3E")}}@-webkit-keyframes mapboxgl-spin{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@-moz-keyframes mapboxgl-spin{0%{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(1turn)}}@-o-keyframes mapboxgl-spin{0%{-o-transform:rotate(0deg)}to{-o-transform:rotate(1turn)}}@-ms-keyframes mapboxgl-spin{0%{-ms-transform:rotate(0deg)}to{-ms-transform:rotate(1turn)}}@keyframes mapboxgl-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}a.mapboxgl-ctrl-logo{width:88px;height:23px;margin:0 0 -4px -4px;display:block;background-repeat:no-repeat;cursor:pointer;overflow:hidden;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='88' height='23' viewBox='0 0 88 23' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd'%3E%3Cdefs%3E%3Cpath id='a' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='b' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='c'%3E%3Crect width='100%25' height='100%25' fill='%23fff'/%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/mask%3E%3Cg opacity='.3' stroke='%23000' stroke-width='3'%3E%3Ccircle mask='url(%23c)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23b' mask='url(%23c)'/%3E%3C/g%3E%3Cg opacity='.9' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/g%3E%3C/svg%3E")}a.mapboxgl-ctrl-logo.mapboxgl-compact{width:23px}@media (-ms-high-contrast:active){a.mapboxgl-ctrl-logo{background-color:transparent;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='88' height='23' viewBox='0 0 88 23' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd'%3E%3Cdefs%3E%3Cpath id='a' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='b' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='c'%3E%3Crect width='100%25' height='100%25' fill='%23fff'/%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/mask%3E%3Cg stroke='%23000' stroke-width='3'%3E%3Ccircle mask='url(%23c)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23b' mask='url(%23c)'/%3E%3C/g%3E%3Cg fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/g%3E%3C/svg%3E")}}@media (-ms-high-contrast:black-on-white){a.mapboxgl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='88' height='23' viewBox='0 0 88 23' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd'%3E%3Cdefs%3E%3Cpath id='a' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='b' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='c'%3E%3Crect width='100%25' height='100%25' fill='%23fff'/%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/mask%3E%3Cg stroke='%23fff' stroke-width='3' fill='%23fff'%3E%3Ccircle mask='url(%23c)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23b' mask='url(%23c)'/%3E%3C/g%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/svg%3E")}}.mapboxgl-ctrl.mapboxgl-ctrl-attrib{padding:0 5px;background-color:hsla(0,0%,100%,.5);margin:0}@media screen{.mapboxgl-ctrl-attrib.mapboxgl-compact{min-height:20px;padding:0;margin:10px;position:relative;background-color:#fff;border-radius:3px 12px 12px 3px}.mapboxgl-ctrl-attrib.mapboxgl-compact:hover{padding:2px 24px 2px 4px;visibility:visible;margin-top:6px}.mapboxgl-ctrl-bottom-left>.mapboxgl-ctrl-attrib.mapboxgl-compact:hover,.mapboxgl-ctrl-top-left>.mapboxgl-ctrl-attrib.mapboxgl-compact:hover{padding:2px 4px 2px 24px;border-radius:12px 3px 3px 12px}.mapboxgl-ctrl-attrib.mapboxgl-compact .mapboxgl-ctrl-attrib-inner{display:none}.mapboxgl-ctrl-attrib.mapboxgl-compact:hover .mapboxgl-ctrl-attrib-inner{display:block}.mapboxgl-ctrl-attrib.mapboxgl-compact:after{content:"";cursor:pointer;position:absolute;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd'%3E%3Cpath d='M4 10a6 6 0 1012 0 6 6 0 10-12 0m5-3a1 1 0 102 0 1 1 0 10-2 0m0 3a1 1 0 112 0v3a1 1 0 11-2 0'/%3E%3C/svg%3E");background-color:hsla(0,0%,100%,.5);width:24px;height:24px;box-sizing:border-box;border-radius:12px}.mapboxgl-ctrl-bottom-right>.mapboxgl-ctrl-attrib.mapboxgl-compact:after{bottom:0;right:0}.mapboxgl-ctrl-top-right>.mapboxgl-ctrl-attrib.mapboxgl-compact:after{top:0;right:0}.mapboxgl-ctrl-top-left>.mapboxgl-ctrl-attrib.mapboxgl-compact:after{top:0;left:0}.mapboxgl-ctrl-bottom-left>.mapboxgl-ctrl-attrib.mapboxgl-compact:after{bottom:0;left:0}}@media screen and (-ms-high-contrast:active){.mapboxgl-ctrl-attrib.mapboxgl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd' fill='%23fff'%3E%3Cpath d='M4 10a6 6 0 1012 0 6 6 0 10-12 0m5-3a1 1 0 102 0 1 1 0 10-2 0m0 3a1 1 0 112 0v3a1 1 0 11-2 0'/%3E%3C/svg%3E")}}@media screen and (-ms-high-contrast:black-on-white){.mapboxgl-ctrl-attrib.mapboxgl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd'%3E%3Cpath d='M4 10a6 6 0 1012 0 6 6 0 10-12 0m5-3a1 1 0 102 0 1 1 0 10-2 0m0 3a1 1 0 112 0v3a1 1 0 11-2 0'/%3E%3C/svg%3E")}}.mapboxgl-ctrl-attrib a{color:rgba(0,0,0,.75);text-decoration:none}.mapboxgl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.mapboxgl-ctrl-attrib .mapbox-improve-map{font-weight:700;margin-left:2px}.mapboxgl-attrib-empty{display:none}.mapboxgl-ctrl-scale{background-color:hsla(0,0%,100%,.75);font-size:10px;border:2px solid #333;border-top:#333;padding:0 5px;color:#333;box-sizing:border-box}.mapboxgl-popup{position:absolute;top:0;left:0;display:-webkit-flex;display:flex;will-change:transform;pointer-events:none}.mapboxgl-popup-anchor-top,.mapboxgl-popup-anchor-top-left,.mapboxgl-popup-anchor-top-right{-webkit-flex-direction:column;flex-direction:column}.mapboxgl-popup-anchor-bottom,.mapboxgl-popup-anchor-bottom-left,.mapboxgl-popup-anchor-bottom-right{-webkit-flex-direction:column-reverse;flex-direction:column-reverse}.mapboxgl-popup-anchor-left{-webkit-flex-direction:row;flex-direction:row}.mapboxgl-popup-anchor-right{-webkit-flex-direction:row-reverse;flex-direction:row-reverse}.mapboxgl-popup-tip{width:0;height:0;border:10px solid transparent;z-index:1}.mapboxgl-popup-anchor-top .mapboxgl-popup-tip{-webkit-align-self:center;align-self:center;border-top:none;border-bottom-color:#fff}.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip{-webkit-align-self:flex-start;align-self:flex-start;border-top:none;border-left:none;border-bottom-color:#fff}.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip{-webkit-align-self:flex-end;align-self:flex-end;border-top:none;border-right:none;border-bottom-color:#fff}.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip{-webkit-align-self:center;align-self:center;border-bottom:none;border-top-color:#fff}.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip{-webkit-align-self:flex-start;align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip{-webkit-align-self:flex-end;align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.mapboxgl-popup-anchor-left .mapboxgl-popup-tip{-webkit-align-self:center;align-self:center;border-left:none;border-right-color:#fff}.mapboxgl-popup-anchor-right .mapboxgl-popup-tip{-webkit-align-self:center;align-self:center;border-right:none;border-left-color:#fff}.mapboxgl-popup-close-button{position:absolute;right:0;top:0;border:0;border-radius:0 3px 0 0;cursor:pointer;background-color:transparent}.mapboxgl-popup-close-button:hover{background-color:rgba(0,0,0,.05)}.mapboxgl-popup-content{position:relative;background:#fff;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.1);padding:10px 10px 15px;pointer-events:auto}.mapboxgl-popup-anchor-top-left .mapboxgl-popup-content{border-top-left-radius:0}.mapboxgl-popup-anchor-top-right .mapboxgl-popup-content{border-top-right-radius:0}.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-content{border-bottom-left-radius:0}.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-content{border-bottom-right-radius:0}.mapboxgl-popup-track-pointer{display:none}.mapboxgl-popup-track-pointer *{pointer-events:none;user-select:none}.mapboxgl-map:hover .mapboxgl-popup-track-pointer{display:flex}.mapboxgl-map:active .mapboxgl-popup-track-pointer{display:none}.mapboxgl-marker{position:absolute;top:0;left:0;will-change:transform}.mapboxgl-user-location-dot,.mapboxgl-user-location-dot:before{background-color:#1da1f2;width:15px;height:15px;border-radius:50%}.mapboxgl-user-location-dot:before{content:"";position:absolute;-webkit-animation:mapboxgl-user-location-dot-pulse 2s infinite;-moz-animation:mapboxgl-user-location-dot-pulse 2s infinite;-ms-animation:mapboxgl-user-location-dot-pulse 2s infinite;animation:mapboxgl-user-location-dot-pulse 2s infinite}.mapboxgl-user-location-dot:after{border-radius:50%;border:2px solid #fff;content:"";height:19px;left:-2px;position:absolute;top:-2px;width:19px;box-sizing:border-box;box-shadow:0 0 3px rgba(0,0,0,.35)}@-webkit-keyframes mapboxgl-user-location-dot-pulse{0%{-webkit-transform:scale(1);opacity:1}70%{-webkit-transform:scale(3);opacity:0}to{-webkit-transform:scale(1);opacity:0}}@-ms-keyframes mapboxgl-user-location-dot-pulse{0%{-ms-transform:scale(1);opacity:1}70%{-ms-transform:scale(3);opacity:0}to{-ms-transform:scale(1);opacity:0}}@keyframes mapboxgl-user-location-dot-pulse{0%{transform:scale(1);opacity:1}70%{transform:scale(3);opacity:0}to{transform:scale(1);opacity:0}}.mapboxgl-user-location-dot-stale{background-color:#aaa}.mapboxgl-user-location-dot-stale:after{display:none}.mapboxgl-user-location-accuracy-circle{background-color:rgba(29,161,242,.2);width:1px;height:1px;border-radius:100%}.mapboxgl-crosshair,.mapboxgl-crosshair .mapboxgl-interactive,.mapboxgl-crosshair .mapboxgl-interactive:active{cursor:crosshair}.mapboxgl-boxzoom{position:absolute;top:0;left:0;width:0;height:0;background:#fff;border:2px dotted #202020;opacity:.5}@media print{.mapbox-improve-map{display:none}}
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/utils/map.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/utils/map.ts
new file mode 100644 (file)
index 0000000..abdd27e
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * ============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 mapboxgl from "mapbox-gl";
+import { LatLon } from "../model/LatLon";
+
+
+export const addBaseSource = (map : mapboxgl.Map, name: string) =>{
+
+  if(!map.getSource(name))
+
+    map.addSource(name, {
+        type: 'geojson',
+        data: { type: "FeatureCollection", features: [] }
+    });
+
+}
+
+export const addPoint = (map : mapboxgl.Map, point: LatLon) =>{
+  const json = `{
+    "type": "Feature",
+    "properties": {},
+    "geometry": {
+      "type": "Point",
+      "coordinates": 
+        [${point.longitude}, ${point.latitude}]
+      }
+    }`;
+    
+    
+      (map.getSource("route") as mapboxgl.GeoJSONSource).setData(JSON.parse(json));
+}
+
+export const addBaseLayer = (map: mapboxgl.Map, sourceName: string) =>{
+
+  if(!map.getLayer('line'))
+    map.addLayer({
+        'id': 'line',
+        'type': 'line',
+        'source': sourceName,
+        'layout': {
+          'line-join': 'round',
+          'line-cap': 'round'
+        },
+        'paint': {
+          'line-color': '#88A',
+          'line-width': 6,
+          'line-opacity': 0.75
+        }
+      });
+
+      if(!map.getLayer('points'))
+      map.addLayer({
+        id: 'points',
+        type: 'circle',
+        source: sourceName,
+        paint: {
+          'circle-radius': 5,
+          'circle-color': '#223b53',
+          'circle-stroke-color': '#225ba3',
+          'circle-stroke-width': 3,
+          'circle-opacity': 0.5
+        }
+      });
+}
+
+export const calculateDistanceInMeter = (lat1: number, lon1: number, lat2: number, lon2: number) => {
+    const lonRad1 = toRad(lon1);
+    const latRad1 = toRad(lat1);
+    const lonRad2 = toRad(lon2);
+    const latRad2 = toRad(lat2);
+  
+    const dLon = lonRad2 - lonRad1;
+    const dLat = latRad2 - latRad1;
+    const a = Math.pow(Math.sin(dLat / 2), 2) +
+        Math.cos(latRad1) * Math.cos(latRad2) *
+        Math.pow(Math.sin(dLon / 2), 2);
+  
+    const c = 2 * Math.asin(Math.sqrt(a));
+  
+    return 6378 * c;
+  
+  }
+  
+  function toRad(value: number) {
+    return (value * Math.PI) / 180;
+  }
+  
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/utils/math.ts b/sdnr/wt/odlux/apps/lineOfSightApp/src/utils/math.ts
new file mode 100644 (file)
index 0000000..9e0447b
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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 const max = <T,>(a: T[], p: (v: T) => Number) => a.reduce<T>((m, x) => p(m) > p(x) ? m : x, a[0]);
+export const min = <T,>(a: T[], p: (v: T) => Number) => a.reduce<T>((m, x) => p(m) < p(x) ? m : x, a[0]);
+
+export const isNumber = (value: string|null) =>{
+
+    if(!value){
+      return false;
+    }else{
+      const num = Number(value);
+      return !isNaN(num);
+    }
+  }
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/views/main.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/views/main.tsx
new file mode 100644 (file)
index 0000000..bbe6f34
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * ============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  Map from '../components/map';
+
+function MainView() {
+  return (
+    <div className="App" style={{height:"100%"}}>
+     <Map />
+    </div>
+  );
+}
+
+export default MainView;
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/lineOfSightApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java
new file mode 100644 (file)
index 0000000..43b072c
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 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==========================================================================
+ */
+package org.onap.ccsdk.features.sdnr.wt.odlux.bundles;
+
+import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle;
+import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader;
+
+public class MyOdluxBundle extends OdluxBundle {
+
+    @Override
+    public void initialize() {
+        super.initialize();
+    }
+
+    @Override
+    public void clean() {
+        super.clean();
+    }
+
+    @Override
+    public String getResourceFileContent(String filename) {
+        return super.getResourceFileContent(filename);
+    }
+
+    @Override
+    public boolean hasResource(String filename) {
+        return super.hasResource(filename);
+    }
+
+    @Override
+    public void setBundleName(String bundleName) {
+        super.setBundleName(bundleName);
+    }
+
+    @Override
+    public void setLoader(OdluxBundleLoader loader) {
+        super.setLoader(loader);
+    }
+
+    @Override
+    public String getBundleName() {
+        return super.getBundleName();
+    }
+
+    @Override
+    public OdluxBundleLoader getLoader() {
+        return super.getLoader();
+    }
+
+    public MyOdluxBundle() {
+        super();
+    }
+}
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/lineOfSightApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml
new file mode 100644 (file)
index 0000000..982379d
--- /dev/null
@@ -0,0 +1,9 @@
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+    <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/>
+
+    <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle">
+        <property name="loader" ref="loadersvc"/>
+        <property name="bundleName" value="lineOfSightApp"/>
+        <property name="index" value="130"/>
+    </bean>
+</blueprint>
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/lineOfSightApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java
new file mode 100644 (file)
index 0000000..c319bb1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt
+ * =================================================================================================
+ * Copyright (C) 2019 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==========================================================================
+ */
+package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl;
+import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle;
+
+public class TestBundleRes {
+
+    @Test
+    public void test() {
+        OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance();
+        MyOdluxBundle b = new MyOdluxBundle();
+        b.setLoader(loader);
+        b.setIndex(0);
+        b.setBundleName("abc");
+        b.initialize();
+        assertTrue(loader.getNumberOfBundles()==1);
+        assertNotNull(b.getLoader());
+        assertEquals("abc",b.getBundleName());
+        assertTrue(b.hasResource("test.js"));
+        assertNotNull(b.getResourceFileContent("test.js"));
+        b.clean();
+        assertTrue(loader.getNumberOfBundles()==0);
+    }
+
+}
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/lineOfSightApp/src2/test/resources/test.js
new file mode 100644 (file)
index 0000000..b47fdc3
--- /dev/null
@@ -0,0 +1,5 @@
+asdac sad 
+as
+d 
+sad
+ sadfa
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/tsconfig.json b/sdnr/wt/odlux/apps/lineOfSightApp/tsconfig.json
new file mode 100644 (file)
index 0000000..a66b5d8
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "compilerOptions": {
+    "baseUrl": "./src",
+    "outDir": "./dist",
+    "sourceMap": true,
+    "forceConsistentCasingInFileNames": true,
+    "allowSyntheticDefaultImports": false,
+    "allowUnreachableCode": false,
+    "allowUnusedLabels": false,
+    "noFallthroughCasesInSwitch": true,
+    "noImplicitAny": true,
+    "noImplicitReturns": true,
+    "noImplicitThis": true,
+    "strictNullChecks": true,
+    "pretty": true,
+    "newLine": "LF",
+    "module": "es2015",
+    "target": "es2016",
+    "moduleResolution": "node",
+    "experimentalDecorators": true,
+    "jsx": "preserve",
+    "lib": [
+      "dom",
+      "es2015",
+      "es2016"
+    ],
+    "types": [
+      "prop-types",
+      "react",
+      "react-dom"
+    ]
+  },
+  "exclude": [
+    "dist",
+    "node_modules"
+  ]
+}
diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/webpack.config.js b/sdnr/wt/odlux/apps/lineOfSightApp/webpack.config.js
new file mode 100644 (file)
index 0000000..54ba1b4
--- /dev/null
@@ -0,0 +1,211 @@
+/**
+ * Webpack 4 configuration file
+ * see https://webpack.js.org/configuration/
+ * see https://webpack.js.org/configuration/dev-server/
+ */
+
+"use strict";
+
+const path = require("path");
+const webpack = require("webpack");
+const CopyWebpackPlugin = require("copy-webpack-plugin");
+const TerserPlugin = require('terser-webpack-plugin');
+
+// const __dirname = (path => path.replace(/^([a-z]\:)/, c => c.toUpperCase()))(process.__dirname());
+
+module.exports = (env) => {
+  const distPath = path.resolve(__dirname, env === "release" ? "." : "../..", "dist");
+  const frameworkPath = path.resolve(__dirname, env === "release" ? "../../framework" : "../..", "dist");
+  return [{
+    name: "App",
+
+    mode: "none", //disable default behavior
+
+    target: "web",
+
+    context: path.resolve(__dirname, "src"),
+
+    entry: {
+      lineOfSightApp: ["./pluginLineOfSight.tsx"]
+    },
+
+    devtool: env === "release" ? false : "source-map",
+
+    resolve: {
+      extensions: [".ts", ".tsx", ".js", ".jsx"]
+    },
+
+    output: {
+      path: distPath,
+      filename: "[name].js",
+      library: "[name]",
+      libraryTarget: "umd2",
+      chunkFilename: "[name].js"
+    },
+    module: {
+      rules: [{
+        test: /\.tsx?$/,
+        exclude: /node_modules/,
+        use: [{
+          loader: "babel-loader"
+        }, {
+          loader: "ts-loader"
+        }]
+      }, {
+        test: /\.jsx?$/,
+        exclude: /node_modules/,
+        use: [{
+          loader: "babel-loader"
+        }]
+      },
+       {
+        test: /\.(png|gif|jpg|svg)$/,
+        use: [{
+          loader: 'url-loader',
+          options: {
+            limit: 10000,
+            name: './icons/[hash].[ext]'
+          }
+        }]
+      },
+      {
+        test: /\.css$/i,
+        use: ["style-loader", "css-loader"],
+      },
+    ]
+    },
+
+    optimization: {
+      noEmitOnErrors: true,
+      namedModules: env !== "release",
+      minimize: env === "release",
+      minimizer: env !== "release" ? [] : [new TerserPlugin({
+        terserOptions: {
+          warnings: false, // false, true, "verbose"
+          compress: {
+            drop_console: true,
+            drop_debugger: true,
+          }
+        }
+      })],
+    },
+
+    plugins: [
+      new webpack.DllReferencePlugin({
+        context: path.resolve(__dirname, "../../framework/src"),
+        manifest: require(path.resolve(frameworkPath, "vendor-manifest.json")),
+        sourceType: "umd2"
+      }),
+      new webpack.DllReferencePlugin({
+        context: path.resolve(__dirname, "../../framework/src"),
+        manifest: require(path.resolve(frameworkPath, "app-manifest.json")),
+        sourceType: "umd2"
+      }),
+      ...(env === "release") ? [
+        new webpack.DefinePlugin({
+          "process.env": {
+            NODE_ENV: "'production'",
+            VERSION: JSON.stringify(require("./package.json").version)
+          }
+        }),
+      ] : [
+          new webpack.DefinePlugin({
+            "process.env": {
+              NODE_ENV: "'development'",
+              VERSION: JSON.stringify(require("./package.json").version)
+            }
+          }),
+          new CopyWebpackPlugin([{
+            from: 'index.html',
+            to: distPath
+          }]),
+        ]
+    ],
+
+    devServer: {
+      public: "http://localhost:3100",
+      contentBase: frameworkPath,
+
+      compress: true,
+      headers: {
+        "Access-Control-Allow-Origin": "*"
+      },
+      host: "0.0.0.0",
+      port: 3100,
+      disableHostCheck: true,
+      historyApiFallback: true,
+      inline: true,
+      hot: false,
+      quiet: false,
+      stats: {
+        colors: true
+      },
+      proxy: {
+        "/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
+        },
+        "/database/": {
+          target: "http://sdnr:8181",
+          secure: false
+        },
+        "/restconf/": {
+          target: "http://sdnr:8181",
+          secure: false
+        },
+        "/rests/": {
+          target: "http://sdnr:8181",
+          secure: false
+        },
+        "/topology/": {
+          target: "http://localhost:3002",
+          secure: false
+        },
+        "/sitedoc/": {
+          target: "http://10.20.35.184:3002",
+          secure: false,
+          pathRewrite(pathname) {
+            return pathname.replace(/^\/sitedoc/, '/topology/stadok')
+          }
+        },
+        "/terrain/": {
+          target: "http://10.20.11.163:5200",
+          secure: false,
+          pathRewrite(pathname) {
+            return pathname.replace(/^\/terrain/, '/')
+          }
+        },
+        "/tiles/": {
+          target: "http://tile.openstreetmap.org",
+          secure: false,
+          pathRewrite(pathname) {
+            return pathname.replace(/^\/tiles/, '')
+          }
+        },
+        "/help/": {
+          target: "http://sdnr:8181",
+          secure: false
+        },
+        "/websocket": {
+          target: "http://sdnr:8181",
+          ws: true,
+          changeOrigin: true,
+          secure: false
+        }
+      }
+
+    }
+  }];
+}
index 0849058..d499ec2 100644 (file)
@@ -116,60 +116,45 @@ export class UpdateWorstMonthRainAction extends Action {
     }
 }
 
-export class UpdateEIRPAction extends Action {
-    constructor(public eirpA: number,public eirpB: number) {
+
+export class UpdateAntennaGainAction extends Action {
+    constructor(public antennaGainA: number, public antennaGainB: number) {
         super();
     }
 }
-export class UpdateAntennaGainAction extends Action {
-    constructor(public antennaGainList: string[]) {
+export class updateAntennaNameAction extends Action {
+    constructor(public antennaNameA: string, public antennaNameB: string) {
         super();
     }
 }
-export class UpdateAntennaListAction extends Action {
-    constructor(public antennaList: string[]) {
+export class UpdateTxPowerAction extends Action {
+    constructor(public txPowerA: string | null, public txPowerB: string | null) {
         super();
     }
 }
-export class UpdateAntennaAction extends Action {
-    constructor(public antennaA: string | null, public antennaB : string | null) {
+export class UpdateRxSensitivityAction extends Action {
+    constructor(public rxSensitivityA: string | null, public rxSensitivityB: string | null) {
         super();
     }
 }
-export class UpdateRadioAttributesAction extends Action {
-    constructor(public som: number , public eirpA : number, public eirpB : number) {
+export class UpdateWaveguideLossAction extends Action {
+    constructor(public waveguideLossA: number, public waveguideLossB: number) {
         super();
     }
 }
-export class UpdateTxPowerAction extends Action {
-    constructor(public txPowerA: string | null , public txPowerB : string | null) {
+
+export class UpdateEIRPAction extends Action {
+    constructor(public eirpA: number, public eirpB: number) {
         super();
     }
 }
-export class UpdateRxSensitivityAction extends Action {
-    constructor(public rxSensitivityA: string | null , public rxSensitivityB : string | null) {
+export class UpdateRxPowerAction extends Action {
+    constructor(public rxPowerA: number, public rxPowerB: number) {
         super();
     }
 }
-
-
-export const updateAntennaList = (frequency: number) => async (dispatcher: Dispatch, getState: () => IApplicationStoreState) => {
-    let antennaList: string[] = []
-    let antennaDiameterList: string[] = []
-    let antennaGainList :string[] =[]
-    //switch case here     frequency = 26? antennaList.push
-    switch (frequency) {
-        case 7: antennaList.push('ANDREW VHLPX2.5-7W', 'ANDREW VHLPX3-7W', 'ANDREW VHLPX4-7W', 'ANDREW VHLPX6-7W' ), antennaDiameterList.push('0.6','0.9','1.2','1.8'), antennaGainList.push('33.90','35.50','37.30','40.61'); break
-        case 11: antennaList.push('ANDREW VHLPX2-11W', 'ANDREW VHLPX3-11W', 'ANDREW VHLPX4-11W'), antennaDiameterList.push('0.6','0.9','1.2'), antennaGainList.push('34.50','38.4','40.70');break
-        case 15: antennaList.push('ANDREW VHLPX1-15', 'ANDREW VHLPX2-15', 'ANDREW VHLPX3-15', 'ANDREW VHLPX4-15'), antennaDiameterList.push('0.3','0.6','0.9','1.2'), antennaGainList.push('32.00','36.80','41.11','42.90');break
-        case 23: antennaList.push('ANDREW VHLPX1-23', 'ANDREW VHLPX2-23', 'ANDREW VHLPX3-23', 'ANDREW VHLPX4-23'), antennaDiameterList.push('0.3','0.6','0.9','1.2'), antennaGainList.push('35.30','40.21','44.80','46.71');break
-        case 26: antennaList.push('ANDREW VHLPX1-26', 'ANDREW VHLPX2-26', 'ANDREW VHLPX3-26'), antennaDiameterList.push('0.3','0.6','0.9'), antennaGainList.push('36.61','40.21','41.21','45.80');break
-        case 28: antennaList.push('ANDREW VHLPX1-28', 'ANDREW VHLPX2-28'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('38.11','42.21');break
-        case 38: antennaList.push('ANDREW VHLPX1-38', 'ANDREW VHLPX2-38'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('40.11','45.21');break
-        case 42: antennaList.push('ANDREW VHLPX1-42-XXX/D', 'ANDREW VHLPX2-42-XXX/A'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('40.80','46.00');break
-        case 80: antennaList.push('Radio Waves HPCPE-80', 'Radio Waves HPLP2-80'), antennaDiameterList.push('0.3','0.6'), antennaGainList.push('43.80','50.80');break
-    }
-    dispatcher(new UpdateAntennaListAction(antennaList))
-    dispatcher(new UpdateAntennaGainAction(antennaGainList))
+export class UpdateSomAction extends Action {
+    constructor(public somA: number, public somB:number) {
+        super();
+    }
 }
-
index edfad05..01512eb 100644 (file)
@@ -21,7 +21,7 @@ import { combineActionHandler } from '../../../../framework/src/flux/middleware'
 // ** do not remove **
 import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore';
 import { IActionHandler } from '../../../../framework/src/flux/action';;
-import { UpdateLinkIdAction, UpdateFrequencyAction , UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, updateHideForm, UpdateFslCalculation, UpdateSiteAction, UpdateDistanceAction, isCalculationServerReachableAction, UpdatePolAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction, UpdateEIRPAction,  UpdateAntennaAction, UpdateAntennaListAction, UpdateAntennaGainAction, UpdateTxPowerAction, UpdateRxSensitivityAction} from '../actions/commonLinkCalculationActions';
+import { UpdateLinkIdAction, UpdateFrequencyAction , UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, updateHideForm, UpdateFslCalculation, UpdateSiteAction, UpdateDistanceAction, isCalculationServerReachableAction, UpdatePolAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction, UpdateEIRPAction, UpdateAntennaGainAction, UpdateTxPowerAction, UpdateRxSensitivityAction, updateAntennaNameAction, UpdateWaveguideLossAction, UpdateRxPowerAction, UpdateSomAction} from '../actions/commonLinkCalculationActions';
 
 declare module '../../../../framework/src/store/applicationStore' {
   interface IApplicationStoreState {
@@ -60,15 +60,18 @@ export type ILinkCalculationAppStateState= {
   eirpB: number, 
   antennaGainA: number,
   antennaGainB :number,
-  antennaList:string[],
-  antennaGainList:string[],
-  antennaA: string,
-  antennaB:string,
-  systemOperatingMargin : number,
+  antennaNameA: string,
+  antennaNameB:string,
+  systemOperatingMarginA : number,
+  systemOperatingMarginB : number,
   txPowerA : string,
   txPowerB: string,
   rxSensitivityA : string,
-  rxSensitivityB: string
+  rxSensitivityB: string, 
+  waveguideLossA : number, 
+  waveguideLossB: number,
+  rxPowerA :number, 
+  rxPowerB: number
 }
 
 const initialState: ILinkCalculationAppStateState ={
@@ -98,19 +101,20 @@ const initialState: ILinkCalculationAppStateState ={
   eirpB: 0, 
   antennaGainA :0,
   antennaGainB :0,
-  antennaList:[],
-  antennaGainList:[],
-  antennaA: '0',
-  antennaB:'0',
-  systemOperatingMargin : 0,
+  antennaNameA: '',
+  antennaNameB:'',
+  systemOperatingMarginA : 0,
+  systemOperatingMarginB : 0,
   txPowerA : '0',
   txPowerB: '0', 
   rxSensitivityA: '0',
-  rxSensitivityB: '0'
+  rxSensitivityB: '0', 
+  waveguideLossA : 0,
+  waveguideLossB: 0, 
+  rxPowerA : 0,
+  rxPowerB: 0
 }
 
-
-
 export const LinkCalculationHandler: IActionHandler<ILinkCalculationAppStateState> = (state=initialState, action) => {
     
   if(action instanceof UpdateLinkIdAction){
@@ -156,17 +160,12 @@ export const LinkCalculationHandler: IActionHandler<ILinkCalculationAppStateStat
   else if (action instanceof UpdateWorstMonthRainAction){
     state = Object.assign({}, state, {month:action.month})
   }
-  else if (action instanceof UpdateEIRPAction){
-    state = Object.assign({}, state, {eirpA:action.eirpA, eirpB:action.eirpB})
-  }
+  
   else if (action instanceof UpdateAntennaGainAction){
-    state = Object.assign({}, state, {antennaGainList:action.antennaGainList})
-  }
-  else if (action instanceof UpdateAntennaListAction){
-    state = Object.assign({}, state, {antennaList:action.antennaList})
+    state = Object.assign({}, state, {antennaGainA:action.antennaGainA,antennaGainB:action.antennaGainB})
   }
-  else if (action instanceof UpdateAntennaAction){
-    state = Object.assign({}, state, {antennaA:action.antennaA == null ? state.antennaA : action.antennaA , antennaB: action.antennaB == null? state.antennaB : action.antennaB})
+  else if (action instanceof updateAntennaNameAction){
+    state = Object.assign({}, state, {antennaNameA:action.antennaNameA, antennaNameB: action.antennaNameB})
   }
   else if (action instanceof UpdateTxPowerAction){
     state = Object.assign({}, state, {txPowerA:action.txPowerA == null ? state.txPowerA : action.txPowerA , txPowerB: action.txPowerB == null? state.txPowerB : action.txPowerB})
@@ -174,6 +173,19 @@ export const LinkCalculationHandler: IActionHandler<ILinkCalculationAppStateStat
   else if (action instanceof UpdateRxSensitivityAction){
     state = Object.assign({}, state, {rxSensitivityA:action.rxSensitivityA == null ? state.rxSensitivityA : action.rxSensitivityA , rxSensitivityB: action.rxSensitivityB == null? state.rxSensitivityB : action.rxSensitivityB})
   }
+  else if (action instanceof UpdateWaveguideLossAction){
+    state = Object.assign({}, state, {waveguideLossA:action.waveguideLossA, waveguideLossB: action.waveguideLossB})
+  }
+  else if (action instanceof UpdateEIRPAction){
+    state = Object.assign({}, state, {eirpA:action.eirpA, eirpB:action.eirpB})
+  }
+  else if (action instanceof UpdateRxPowerAction){
+    state = Object.assign({}, state, {rxPowerA:action.rxPowerA, rxPowerB:action.rxPowerB})
+  }
+  else if (action instanceof UpdateSomAction){
+    state = Object.assign({}, state, {systemOperatingMarginA:action.somA , systemOperatingMarginB :action.somB})
+  }
+  
 
   return state
 }
index 2023ae3..edcbd25 100644 (file)
   <script type="text/javascript" src="./config.js"></script>
   <script>
     // run the application
-    require(["app","connectApp", "linkCalculationApp", "networkMapApp"], function (app, connectApp, linkCalculationApp,networkMapApp) {
+    require(["app","connectApp", "linkCalculationApp", "networkMapApp" , "lineOfSightApp"], function (app, connectApp, linkCalculationApp,networkMapApp, lineOfSightApp) {
       connectApp.register();
       linkCalculationApp.register();
       networkMapApp.register();
+      lineOfSightApp.register();
       app("./app.tsx").runApplication();
     });
   </script>
index f86b22a..a15bf03 100644 (file)
@@ -24,11 +24,11 @@ import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-
 import { faBookOpen } from '@fortawesome/free-solid-svg-icons'; // select app icon
 import applicationManager from '../../../framework/src/services/applicationManager';
 
-import  LinkCalculation  from './views/linkCalculationComponent';
+import LinkCalculation from './views/linkCalculationComponent';
 import LinkCalculationAppRootHandler from './handlers/linkCalculationAppRootHandler';
 import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect';
 import { IApplicationStoreState } from "../../../framework/src/store/applicationStore";
-import { UpdateLinkIdAction, UpdateLatLonAction, updateHideForm, UpdateSiteAction, UpdateDistanceAction, isCalculationServerReachableAction, updateAltitudeAction } from "./actions/commonLinkCalculationActions";
+import { UpdateLinkIdAction, UpdateLatLonAction, updateHideForm, UpdateSiteAction, UpdateDistanceAction, isCalculationServerReachableAction, updateAltitudeAction, updateAntennaNameAction, UpdateAntennaGainAction, UpdateWaveguideLossAction } from "./actions/commonLinkCalculationActions";
 
 
 let currentLinkId: string | null = null;
@@ -41,23 +41,29 @@ const mapProps = (state: IApplicationStoreState) => ({
 const mapDisp = (dispatcher: IDispatcher) => ({
   updateLinkId: (mountId: string) => dispatcher.dispatch(new UpdateLinkIdAction(mountId)),
 
-  updateSiteName: (siteNameA?:any, siteNameB?:any)=>{
+  updateSiteName: (siteNameA?: any, siteNameB?: any) => {
     dispatcher.dispatch(new UpdateSiteAction(siteNameA, siteNameB))
   },
-  updateDistance :(distance:number) =>{
+  updateDistance: (distance: number) => {
     dispatcher.dispatch(new UpdateDistanceAction(distance))
   },
-  updateLatLon : (Lat1:number, Lon1:number, Lat2:number, Lon2:number)=> {
+  updateLatLon: (Lat1: number, Lon1: number, Lat2: number, Lon2: number) => {
 
     dispatcher.dispatch(new UpdateLatLonAction(Lat1, Lon1, Lat2, Lon2))
-    dispatcher.dispatch(new updateHideForm (true))
+    dispatcher.dispatch(new updateHideForm(true))
   },
-  updateAltitude : (amslA:number, aglA:number, amslB:number, aglB:number) => {
-    dispatcher.dispatch(new updateAltitudeAction(amslA,aglA,amslB,aglB))
+  updateAltitude: (amslA: number, aglA: number, amslB: number, aglB: number) => {
+    dispatcher.dispatch(new updateAltitudeAction(amslA, aglA, amslB, aglB))
+  },
+  updateAntennaName: (antennaNameA: string, antennaNameB: string) => {
+    dispatcher.dispatch(new updateAntennaNameAction(antennaNameA, antennaNameB))
+  },
+  updateAntennaGainAction: (antennaGainA: number, antennaGainB: number) => {
+    dispatcher.dispatch(new UpdateAntennaGainAction(antennaGainA, antennaGainB))
+  },
+  updateWaveguideLossAction: (waveguideLossA: number, waveguideLossB: number) => {
+    dispatcher.dispatch(new UpdateWaveguideLossAction(waveguideLossA, waveguideLossB))
   }
-  // UpdateConectivity : (reachable:boolean) => {
-  //   dispatcher.dispatch (new isCalculationServerReachableAction (reachable))
-  // }
 });
 
 
@@ -70,45 +76,68 @@ const LinkCalculationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComp
     lastUrl = props.location.pathname;
     linkId = getLinkId(lastUrl);
 
-    const data= props.location.search
+    const data = props.location.search
+
 
-    
 
-    if (data !== undefined && data.length>0){
+    if (data !== undefined && data.length > 0) {
 
-    
-    const lat1 = data.split('&')[0].split('=')[1]
-    const lon1 = data.split('&')[1].split('=')[1]
-    const lat2 = data.split('&')[2].split('=')[1]
-    const lon2 = data.split('&')[3].split('=')[1]
 
-    const siteNameA = data.split('&')[4].split('=')[1]
-    const siteNameB = data.split('&')[5].split('=')[1]
+      const lat1 = data.split('&')[0].split('=')[1]
+      const lon1 = data.split('&')[1].split('=')[1]
+      const lat2 = data.split('&')[2].split('=')[1]
+      const lon2 = data.split('&')[3].split('=')[1]
 
-    const distance = data.split('&')[8].split('=')[1]
+      const siteNameA = data.split('&')[4].split('=')[1]
+      const siteNameB = data.split('&')[5].split('=')[1]
 
-    const amslA = data.split('&')[9].split('=')[1]
-    const aglA = data.split('&')[10].split('=')[1]
+      const distance = data.split('&')[8].split('=')[1]
 
-    const amslB = data.split('&')[11].split('=')[1]
-    const aglB = data.split('&')[12].split('=')[1]
+      const amslA = data.split('&')[9].split('=')[1]
+      const aglA = data.split('&')[10].split('=')[1]
 
+      const amslB = data.split('&')[11].split('=')[1]
+      const aglB = data.split('&')[12].split('=')[1]
 
-    props.updateSiteName(String(siteNameA), String(siteNameB))
+      const antennaNameA = data.split('&')[13].split('=')[1].replace("%20", " ")
+      const antennaGainA = data.split('&')[14].split('=')[1]
+      const waveguideLossA = data.split('&')[15].split('=')[1]
+      const antennaNameB = data.split('&')[16].split('=')[1].replace("%20", " ")
+      const antennaGainB = data.split('&')[17].split('=')[1]
+      const waveguideLossB = data.split('&')[18].split('=')[1]
 
-    props.updateDistance(Number(distance))
 
-    props.updateLatLon(Number(lat1),Number(lon1),Number(lat2),Number(lon2))
+      if (siteNameA !== null && siteNameB !== null) {
+        props.updateSiteName(String(siteNameA), String(siteNameB))
+      }
 
-    props.updateAltitude (Number(amslA), Number(aglA), Number(amslB), Number(aglB))
-    
+      if (Number(distance) !== null) {
+        props.updateDistance(Number(distance))
+      }
+      if (Number(lat1) >= -90 && Number(lat2) >= -90 && Number(lat1) <= 90 && Number(lat2) <= 90 && Number(lon1) >= -180 && Number(lon2) >= -180 && Number(lon1) <= 180 && Number(lon2) <= 180) {
+        props.updateLatLon(Number(lat1), Number(lon1), Number(lat2), Number(lon2))
+      }
+      if (Number(amslA)> 0 && Number(amslB)> 0) {
+        if (Number(aglA)>= Number(amslA) && Number(aglB)>= Number(amslB)) {
+          props.updateAltitude(Number(amslA), Number(aglA), Number(amslB), Number(aglB))
+        }
+      }
+      if (antennaNameA && antennaNameB.length) {
+        props.updateAntennaName(String(antennaNameA), String(antennaNameB))
+      }
+      if (Number(antennaGainA) > 0 && Number(antennaGainA) > 0) {
+        props.updateAntennaGainAction(Number(antennaGainA), Number(antennaGainB))
+      }
+      if(Number(waveguideLossA) !== null, Number(waveguideLossB) !== null){
+        props.updateWaveguideLossAction(Number(waveguideLossA),Number(waveguideLossB) )
+      }
     }
-    
+
 
     if (currentLinkId !== linkId) { // new element is loaded
       currentLinkId = linkId;
       props.updateLinkId(currentLinkId);
-    } 
+    }
   }, []);
 
   // called when component gets updated
@@ -126,7 +155,7 @@ const LinkCalculationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComp
   const getLinkId = (lastUrl: string) => {
     let index = lastUrl.lastIndexOf("linkCalculation/");
     if (index >= 0) {
-      linkId = lastUrl.substr(index+16);
+      linkId = lastUrl.substr(index + 16);
     } else {
       linkId = "";
     }
@@ -134,7 +163,7 @@ const LinkCalculationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComp
     return linkId;
   }
 
-  
+
   return (
     <LinkCalculation />
   );
index 0639262..e3eaa6b 100644 (file)
@@ -24,7 +24,7 @@ import { TextField, Tabs, Tab, Typography, AppBar, Button, Tooltip, Checkbox, Ta
 import './Style.scss'
 
 import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore";
-import { UpdateFrequencyAction, UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, UpdateFslCalculation, isCalculationServerReachableAction, UpdatePolAction, UpdateDistanceAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction, updateAntennaList, UpdateAntennaAction, UpdateRadioAttributesAction, UpdateTxPowerAction, UpdateRxSensitivityAction } from "../actions/commonLinkCalculationActions";
+import { UpdateFrequencyAction, UpdateLatLonAction, UpdateRainAttAction, UpdateRainValAction, UpdateFslCalculation, isCalculationServerReachableAction, UpdatePolAction, UpdateDistanceAction, updateAltitudeAction, UpdateAbsorptionLossAction, UpdateWorstMonthRainAction, UpdateTxPowerAction, UpdateRxSensitivityAction, UpdateEIRPAction, UpdateRxPowerAction, UpdateSomAction } from "../actions/commonLinkCalculationActions";
 import { faPlaneArrival, faAlignCenter } from "@fortawesome/free-solid-svg-icons";
 import ConnectionInfo from '../components/connectionInfo'
 import { red } from "@material-ui/core/colors";
@@ -55,15 +55,22 @@ const mapProps = (state: IApplicationStoreState) => ({
   absorptionOxygen: state.linkCalculation.calculations.absorptionOxygen,
   absorptionWater: state.linkCalculation.calculations.absorptionWater,
   month: state.linkCalculation.calculations.month,
-  eirpSiteA: state.linkCalculation.calculations.eirpA,
-  eirpSiteB: state.linkCalculation.calculations.eirpB,
+  eirpA: state.linkCalculation.calculations.eirpA,
+  eirpB: state.linkCalculation.calculations.eirpB,
   antennaGainA: state.linkCalculation.calculations.antennaGainA,
   antennaGainB: state.linkCalculation.calculations.antennaGainB,
-  antennaList: state.linkCalculation.calculations.antennaList,
-  antennaGainList: state.linkCalculation.calculations.antennaGainList,
-  antennaA: state.linkCalculation.calculations.antennaA,
-  antennaB: state.linkCalculation.calculations.antennaB,
-  systemOperatingMargin : state.linkCalculation.calculations.systemOperatingMargin
+  antennaNameA: state.linkCalculation.calculations.antennaNameA,
+  antennaNameB: state.linkCalculation.calculations.antennaNameB,
+  systemOperatingMarginA: state.linkCalculation.calculations.systemOperatingMarginA,
+  systemOperatingMarginB: state.linkCalculation.calculations.systemOperatingMarginB,
+  waveguideLossA: state.linkCalculation.calculations.waveguideLossA,
+  waveguideLossB: state.linkCalculation.calculations.waveguideLossB,
+  rxPowerA: state.linkCalculation.calculations.rxPowerA,
+  rxPowerB: state.linkCalculation.calculations.rxPowerB,
+  txPowerA: state.linkCalculation.calculations.txPowerA,
+  txPowerB: state.linkCalculation.calculations.txPowerB,
+  rxSensitivityA: state.linkCalculation.calculations.rxSensitivityA,
+  rxSensitivityB: state.linkCalculation.calculations.rxSensitivityB
 
 });
 
@@ -74,7 +81,6 @@ const mapDispatch = (dispatcher: IDispatcher) => ({
   updateFrequency: (frequency: number) => {
 
     dispatcher.dispatch(new UpdateFrequencyAction(frequency))
-    dispatcher.dispatch(updateAntennaList(frequency))
   },
   updateLatLon: (Lat1: number, Lon1: number, Lat2: number, Lon2: number) => {
     dispatcher.dispatch(new UpdateLatLonAction(Lat1, Lon1, Lat2, Lon2))
@@ -114,16 +120,20 @@ const mapDispatch = (dispatcher: IDispatcher) => ({
   UpdateWorstMonthRain: (month: string) => {
     dispatcher.dispatch(new UpdateWorstMonthRainAction(month))
   },
-  UpdateAntenas: (antennaA: string | null, antennaB: string | null) => {
-    dispatcher.dispatch(new UpdateAntennaAction(antennaA, antennaB))
+  UpdateEIRP: (eirpA: number, eirpB: number) => {
+    dispatcher.dispatch(new UpdateEIRPAction(eirpA, eirpB))
   },
-  UpdateRadioAttributes :(som: number, eirpA: number, eirpB: number)=>{
-    dispatcher.dispatch(new UpdateRadioAttributesAction(som,eirpA, eirpB))
+  UpdateRxPower: (rxPowerA: number, rxPowerB: number) => {
+    dispatcher.dispatch(new UpdateRxPowerAction(rxPowerA, rxPowerB))
   },
-  UpdateTxPower :(txPowerA: string | null, txPowerB: string | null)=>{
+  UpdateSom: (somA: number, somB: number) => {
+    dispatcher.dispatch(new UpdateSomAction(somA, somB))
+  },
+
+  UpdateTxPower: (txPowerA: string | null, txPowerB: string | null) => {
     dispatcher.dispatch(new UpdateTxPowerAction(txPowerA, txPowerB))
-  }, 
-  UpdateRxSensitivity :(rxSensitivityA : string | null, rxSensitivityB : string | null)=>{
+  },
+  UpdateRxSensitivity: (rxSensitivityA: string | null, rxSensitivityB: string | null) => {
     dispatcher.dispatch(new UpdateRxSensitivityAction(rxSensitivityA, rxSensitivityB))
   }
 });
@@ -145,6 +155,7 @@ interface initialState {
   attenuationMethodError: string,
   worstmonth: boolean,
   showWM: string,
+  rainMethodErrorState: string
 }
 
 class LinkCalculation extends React.Component<linkCalculationProps, initialState> {
@@ -164,6 +175,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
       antennaTypeError: '',
       worstmonth: false,
       showWM: '',
+      rainMethodErrorState: '0'
     };
   }
 
@@ -246,10 +258,10 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
     }
   }
 
-  linkBudget = (antennaA: string, antennaB: string, transmissionPowerA: number, transmissionPowerB: number) => {
-    fetch(BASE_URL + '/linkbudget/' + antennaA + '/' + antennaB + '/' + transmissionPowerA + '/' + transmissionPowerB)
-    .then(res=>res.json())
-    .then(result => {this.props.UpdateRadioAttributes(result.systemOperatingMargin, result.eirpA, result.eirpB)})
+  linkBudget = (lat1: number, lon1: number, lat2: number, lon2: number, distance: number, frequency: number, absorptionMethod: string, polarization: string, antennaGainA: number, antennaGainB: number, waveguideLossA: number, waveguideLossB: number, transmissionPowerA: number, transmissionPowerB: number, rxSensitivityA: number, rxSensitivityB: number) => {
+    fetch(BASE_URL + '/linkbudget/' + lat1 + ',' + lon1 + ',' + lat2 + ',' + lon2 + '/' + distance + '/' + frequency + '/' + absorptionMethod + '/' + polarization.toUpperCase() + '/' + antennaGainA + '/' + antennaGainB + '/' + waveguideLossA + '/' + waveguideLossB + '/' + transmissionPowerA + '/' + transmissionPowerB + '/' + rxSensitivityA + '/' + rxSensitivityB)
+      .then(res => res.json())
+      .then(result => { this.props.UpdateEIRP(result.eirpA, result.eirpB); this.props.UpdateRxPower(result.receivedPowerA, result.receivedPowerB); this.props.UpdateSom(result.systemOperatingMarginA, result.systemOperatingMarginA) })
   }
 
   formValid = () => {
@@ -260,7 +272,10 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
     this.props.lon2 === 0 ? this.setState({ longitude2Error: 'Enter a number between -180 to 180' }) : null
     this.props.frequency === 0 ? this.setState({ frequencyError: 'Select a frequency' }) : this.setState({ frequencyError: '' })
 
-    this.state.rainMethodDisplay === null && this.props.rainVal === 0 ? this.setState({ rainMethodError: 'Select the rain method' }) : this.setState({ rainMethodError: '' })
+    // this.state.rainMethodDisplay === null && this.props.rainVal === 0 ? this.setState({ rainMethodError: 'Select the rain method' }) : this.setState({ rainMethodError: '' })
+
+    this.state.rainMethodErrorState === '0' ? this.setState({ rainMethodError: 'Select the rain method' }) : this.setState({ rainMethodError: '' })
+
     this.state.absorptionMethod === '0' ? this.setState({ attenuationMethodError: 'Select the attenuation method' }) : this.setState({ attenuationMethodError: '' })
     console.log(this.state);
     console.log(this.props.lat1 !== 0 && this.props.lat2 !== 0 && this.props.lon1 !== 0 && this.props.lon2 !== 0 && this.props.frequency !== 0 && this.state.rainMethodError === '' && this.state.attenuationMethodError === '');
@@ -282,21 +297,18 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
         this.setState({ showWM: ' ' })
         this.props.UpdateWorstMonthRain('')
         this.AbsorptionAtt(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.distance, this.props.frequency, this.state.worstmonth, this.state.absorptionMethod)
+        this.linkBudget(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.distance, this.props.frequency, this.state.absorptionMethod, this.props.polarization!, this.props.antennaGainA, this.props.antennaGainB, this.props.waveguideLossA, this.props.waveguideLossB, Number(this.props.txPowerA), Number(this.props.txPowerB), Number(this.props.rxSensitivityA), Number(this.props.rxSensitivityB))
 
         if (this.state.rainMethodDisplay === true) {
 
           this.manualRain(this.props.rainVal, this.props.frequency, this.props.distance, this.props.polarization!);
         }
         else {
-          // this.updateRainValue(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.state.worstmonth)
           this.rainAttCal(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.frequency, this.props.distance, this.props.polarization!, this.state.worstmonth);
         }
       }
       else {
         this.AbsorptionAtt(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.distance, this.props.frequency, this.state.worstmonth, this.state.absorptionMethod)
-
-        // this.updateRainValue(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.state.worstmonth)
-
         this.rainAttCal(this.props.lat1, this.props.lon1, this.props.lat2, this.props.lon2, this.props.frequency, this.props.distance, this.props.polarization!, this.state.worstmonth);
       }
     }
@@ -432,7 +444,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
           </div>
           <div>
             <div style={{ marginTop: 5 }}>Frequency</div>
-            <div style={{ marginTop: 5 }}> {<select aria-label="select-frequency-in-ghz" className={this.state.frequencyError.length > 0 ? 'error' : 'input'} onChange={(e) => { this.props.updateFrequency(Number(e.target.value)); e.target.value === '0' ? this.setState({ frequencyError: 'select a frequency' }) : this.setState({ frequencyError: '' }); this.props.UpdateAntenas('0', '0') }}>
+            <div style={{ marginTop: 5 }}> {<select aria-label="select-frequency-in-ghz" className={this.state.frequencyError.length > 0 ? 'error' : 'input'} onChange={(e) => { this.props.updateFrequency(Number(e.target.value)); e.target.value === '0' ? this.setState({ frequencyError: 'select a frequency' }) : this.setState({ frequencyError: '' }) }}>
 
               <option value='0' aria-label="none-value" >Select Freq</option>
               <option value='7' aria-label="7" >7 GHz</option>
@@ -452,7 +464,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
           </div>
           <div>
             <div>Rain Model</div>
-            <div> {<select aria-label="select-rain-method" className={this.state.rainMethodError.length > 0 ? 'error' : 'input'} onChange={(e) => { e.target.value === 'itu' ? this.setState({ rainMethodDisplay: false }) : this.setState({ rainMethodDisplay: true }); e.target.value === '0' ? this.setState({ rainMethodError: 'select a Rain model' }) : this.setState({ rainMethodError: '' }) }}>
+            <div> {<select aria-label="select-rain-method" className={this.state.rainMethodError.length > 0 ? 'error' : 'input'} onChange={(e) => {if (e.target.value !== '') { this.setState({ rainMethodErrorState: e.target.value, rainMethodError: '' }) }; e.target.value === 'itu' ? this.setState({ rainMethodDisplay: false }) : this.setState({ rainMethodDisplay: true }) }}>
               <option value='0' aria-label="none-value" >Select Rain Method</option>
               <option value='itu' aria-label="itur8377">ITU-R P.837-7</option>
               <option value='manual' aria-label="manual-entry">Specific Rain</option>
@@ -471,7 +483,7 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
           </div>
           <div>
             <div>Absorption Model</div>
-            <div> {<select aria-label="select-absorption-method" className={this.state.attenuationMethodError.length > 0 ? 'error' : 'input'} onChange={(e) => { if (e.target.value !== '') { this.setState({ absorptionMethod: e.target.value }); this.setState({ attenuationMethodError: '' }) } }}>
+            <div> {<select aria-label="select-absorption-method" className={this.state.attenuationMethodError.length > 0 ? 'error' : 'input'} onChange={(e) => { if (e.target.value !== '') { this.setState({ absorptionMethod: e.target.value, attenuationMethodError: '' }) } }}>
               <option value='0' aria-label="none-value" >Select Absorption Method</option>
               <option value='ITURP67612' aria-label="iturp67612" >ITU-R P.676-12</option>
               <option value='ITURP67611' aria-label="iturp67611"  >ITU-R P.676-11</option>
@@ -489,14 +501,15 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
           </div>
           <div>
             <div>System Operating Margin</div>
-            <div aria-label="system-operating-margin">{this.props.systemOperatingMargin} dB</div>
+            <div aria-label="system-operating-margin">{this.props.systemOperatingMarginA.toFixed(3)} dB</div>
+            <div aria-label="system-operating-margin">{this.props.systemOperatingMarginB.toFixed(3)} dB</div>
           </div>
           <div>
-            <div>Radio Transmitted Power</div>    
-            <div> {<form><input aria-label="site-a-transmitted-power" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => {if (e.target.value !== '') this.props.UpdateTxPower(e.target.value,null) }}
+            <div>Radio Transmitted Power</div>
+            <div> {<form><input aria-label="site-a-transmitted-power" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { if (e.target.value !== '') this.props.UpdateTxPower(e.target.value, null) }}
             >
             </input> dBm </form>} </div>
-            <div> {<form><input aria-label="site-b-transmitted-power" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { if (e.target.value !== '') this.props.UpdateTxPower(null,e.target.value) }}
+            <div> {<form><input aria-label="site-b-transmitted-power" type="number" style={{ width: 70, height: 15, fontSize: 14 }} onChange={(e) => { if (e.target.value !== '') this.props.UpdateTxPower(null, e.target.value) }}
             >
             </input>  dBm  </form>} </div>
           </div>
@@ -509,40 +522,42 @@ class LinkCalculation extends React.Component<linkCalculationProps, initialState
             >
             </input>  dBm  </form>} </div>
           </div>
+          <div>
+            <div>Rx power</div>
+            <div aria-label="site-a-effective-isotropic-radiated-power">{this.props.rxPowerA.toFixed(3)} dBm</div>
+            <div aria-label="site-b-effective-isotropic-radiated-power">{this.props.rxPowerB.toFixed(3)} dBm</div>
+          </div>
         </div>
         <div className='antennaContainer'>
           <div>
             <div></div>
             <div className='antennaFont'>Antenna Settings</div>
           </div>
+
           <div>
             <div>Antenna</div>
+            <div aria-label="site-a-amsl">{this.props.antennaNameA} </div>
+            <div aria-label="site-b-amsl">{this.props.antennaNameB}</div>
+          </div>
 
-            <div> {<select aria-label="site-a-select-antenna" value={this.props.antennaA} style={{ width: 160, height: 22, fontSize: 13 }} className={this.state.antennaTypeError.length > 0 ? 'error' : 'input'} onChange={(e) => { if (e.target.value !== '') { this.props.UpdateAntenas(e.target.value, null); this.setState({ antennaTypeError: '' }) } }}>
-              <option value='0' aria-label="none-value" >Select Antenna</option>
-              {this.props.antennaList.map(antenna => <option value={antenna}>{antenna}</option>)}
-
-            </select>} <div style={{ fontSize: 12, color: 'red' }}>{this.state.antennaTypeError}</div>
-            </div>
-            <div> {<select aria-label="site-b-select-antenna" value={this.props.antennaB} style={{ width: 160, height: 22, fontSize: 13 }} className={this.state.antennaTypeError.length > 0 ? 'error' : 'input'} onChange={(e) => { if (e.target.value !== '') { this.props.UpdateAntenas(null, e.target.value); this.setState({ antennaTypeError: '' }) } }}>
-              <option value='0' aria-label="none-value" >Select Antenna</option>
-              {this.props.antennaList.map(antenna => <option value={antenna}>{antenna}</option>)}
 
-            </select>} <div style={{ fontSize: 12, color: 'red' }}>{this.state.antennaTypeError}</div>
-            </div>
-          </div>
           <div>
             <div>EIRP</div>
-            <div aria-label="site-a-effective-isotropic-radiated-power">{this.props.eirpSiteA} dBm</div>
-            <div aria-label="site-b-effective-isotropic-radiated-power">{this.props.eirpSiteB} dBm</div>
+            <div aria-label="site-a-effective-isotropic-radiated-power">{this.props.eirpA.toFixed(3)} dBm</div>
+            <div aria-label="site-b-effective-isotropic-radiated-power">{this.props.eirpB.toFixed(3)} dBm</div>
           </div>
-          
+
           <div>
             <div>Gain</div>
-            <div aria-label="site-a-antenna-gain" > {this.props.antennaGainList[this.props.antennaList.indexOf(this.props.antennaA)]} dBi</div>
-            <div aria-label="site-b-antenna-gain">{this.props.antennaGainList[this.props.antennaList.indexOf(this.props.antennaB)]} dBi</div>
-
+            <div aria-label="site-a-antenna-gain" > {this.props.antennaGainA} dBi</div>
+            <div aria-label="site-b-antenna-gain">{this.props.antennaGainB} dBi</div>
           </div>
+          <div>
+            <div>Waveguide Loss</div>
+            <div aria-label="site-a-waveguide-loss" > {this.props.waveguideLossA} dB</div>
+            <div aria-label="site-b-waveguide-loss">{this.props.waveguideLossB} dB</div>
+          </div>
+
           <div>
             <div></div>
             <div>{<button aria-label="calculate-button" style={{ color: '#222', fontFamily: 'Arial', boxAlign: 'center', display: 'inline-block', insetInlineStart: '20', alignSelf: 'center' }}
index 515c582..55d98b4 100644 (file)
@@ -165,6 +165,20 @@ module.exports = (env) => {
           ws: true,
           changeOrigin: true,
           secure: false
+        },
+        "/terrain": {
+          target: "http://10.20.11.163:5200",
+          secure: false,
+          pathRewrite(pathname) {
+            return pathname.replace(/^\/terrain/, '')
+          }
+        },
+        "/terrain/": {
+          target: "http://10.20.11.163:5200",
+          secure: false,
+          pathRewrite(pathname) {
+            return pathname.replace(/^\/terrain/, '/')
+          }
         }
       }
 
index 61b7813..b6c14a9 100644 (file)
@@ -34,7 +34,7 @@ const useStyles = makeStyles(theme => ({
         border: "1px solid #ced4da",
         fontSize: 16,
         width: "auto",
-        padding: "5px 26px 5px 12px",
+        padding: "5px 5px 5px 5px",
         transition: theme.transitions.create(["border-color", "box-shadow"]),
     },
     center: {
index da81a53..8fd027f 100644 (file)
@@ -24,7 +24,7 @@
   "author": "Matthias Fischer",\r
   "license": "Apache-2.0",\r
   "peerDependencies": {\r
-    "@types/node": "12.0.0",\r
+    "@types/node": "^12.0.0",\r
     "@types/react": "17.0.3",\r
     "@types/react-dom": "17.0.2",\r
     "@types/react-router-dom": "5.1.7",\r
index 0e7baef..88f6746 100644 (file)
@@ -46,7 +46,7 @@
     <properties>
         <buildtime>${maven.build.timestamp}</buildtime>
         <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
-        <buildno>115.f8b3b3c(21/07/30)</buildno>
+        <buildno>116.8c2f6b7(21/08/05)</buildno>
         <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
     </properties>
 
index 174444f..400ee35 100644 (file)
@@ -46,7 +46,7 @@ type odluxVersion= {version:string,build:string, framework: string,
     permanceHistoryApp: string
   }};
 
-type topologyVersion = {version: string};
+type topologyVersion = {version: string, buildTimestamp: string};
 
 class AboutComponent extends React.Component<any, AboutState> {
   textarea: React.RefObject<HTMLTextAreaElement>;
@@ -91,7 +91,9 @@ class AboutComponent extends React.Component<any, AboutState> {
     }
     else
     {
-      return `| | |\n| --- | --- |\n| Version | ${data.version} |\n`
+      const topologyInfo = `| | |\n| --- | --- |\n| Version | ${data.version} |\n` +
+                           `| Build timestamp | ${data.buildTimestamp} |\n`;
+      return topologyInfo;
     }
   }
 
index 19cdba0..98609e8 100644 (file)
@@ -20,7 +20,8 @@
   ~
   -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <parent>
                                     <type>jar</type>
                                     <overWrite>false</overWrite>
                                 </artifactItem>
+                                <!-- line of sight app-->
+                                <artifactItem>
+                                    <groupId>${project.groupId}</groupId>
+                                    <artifactId>sdnr-wt-odlux-app-lineOfSightApp</artifactId>
+                                    <version>${project.version}</version>
+                                    <type>jar</type>
+                                    <overWrite>false</overWrite>
+                                </artifactItem>
                                 <!-- inventoryApp -->
                                 <artifactItem>
                                     <groupId>${project.groupId}</groupId>
index 81d2352..b47f852 100644 (file)
@@ -1,12 +1,13 @@
-odlux.framework.buildno=112.fc75f5b(21/07/21)
+odlux.framework.buildno=116.8c2f6b7(21/08/05)
 odlux.apps.configurationApp.buildno=109.dbfed60(21/06/23)
-odlux.apps.connectApp.buildno=114.1220e03(21/07/30)
+odlux.apps.connectApp.buildno=116.8c2f6b7(21/08/05)
 odlux.apps.eventLogApp.buildno=108.a60ec28(21/06/11)
 odlux.apps.faultApp.buildno=114.1220e03(21/07/30)
 odlux.apps.helpApp.buildno=108.a60ec28(21/06/11)
 odlux.apps.inventoryApp.buildno=108.a60ec28(21/06/11)
-odlux.apps.linkCalculationApp.buildno=111.b9067b6(21/07/16)
+odlux.apps.linkCalculationApp.buildno=116.8c2f6b7(21/08/05)
 odlux.apps.maintenanceApp.buildno=108.a60ec28(21/06/11)
 odlux.apps.mediatorApp.buildno=108.a60ec28(21/06/11)
 odlux.apps.networkMapApp.buildno=115.f8b3b3c(21/07/30)
+odlux.apps.lineOfSightApp.buildno=116.8c2f6b7(21/08/05)
 odlux.apps.permanceHistoryApp.buildno=81.1c38886(20/12/04)
index 851ebaf..a8c2d76 100644 (file)
@@ -22,7 +22,7 @@
     "@types/glob-to-regexp": "0.4.0",
     "@types/jquery": "3.3.10",
     "@types/jsonwebtoken": "7.2.8",
-    "@types/node": "12.0.0",
+    "@types/node": "^12.0.0",
     "@types/react": "17.0.3",
     "@types/react-dom": "17.0.2",
     "@types/react-router-dom": "5.1.7",
index 0c1f149..03c1e94 100644 (file)
@@ -54,6 +54,7 @@
         <module>apps/configurationApp</module>
         <module>apps/networkMapApp</module>
         <module>apps/linkCalculationApp</module>
+        <module>apps/lineOfSightApp</module>
         <module>apps/app-feature</module>
         <module>apps/app-installer</module>
         <module>installer</module>
index f94f49c..4fb7040 100644 (file)
   resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.6.tgz#dbe8a666156d556ed018e15a4c65f08937c3f628"
   integrity sha512-XHcYvVdbtAxVstjKxuULYqYaWIzHR15yr1pZj4fnGChuBVJlIAp9StJna0ZJNSgxPh4Nac2FL4JM3M11Tm6fqQ==
 
+"@types/d3-array@^2":
+  version "2.12.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-2.12.3.tgz#8d16d51fb04ad5a5a8ebe14eb8263a579f1efdd1"
+  integrity sha512-hN879HLPTVqZV3FQEXy7ptt083UXwguNbnxdTGzVW4y4KjX5uyNKljrQixZcSJfLyFirbpUokxpXtvR+N5+KIg==
+
+"@types/d3-axis@^2":
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-2.1.3.tgz#348cca877f6643030aa8c866d08ccae06821a0e2"
+  integrity sha512-QjXjwZ0xzyrW2ndkmkb09ErgWDEYtbLBKGui73QLMFm3woqWpxptfD5Y7vqQdybMcu7WEbjZ5q+w2w5+uh2IjA==
+  dependencies:
+    "@types/d3-selection" "^2"
+
+"@types/d3-brush@^2":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-2.1.2.tgz#c75890d1ccaef24fba1811daae3f896c1806418b"
+  integrity sha512-DnZmjdK1ycX1CMiW9r5E3xSf1tL+bp3yob1ON8bf0xB0/odfmGXeYOTafU+2SmU1F0/dvcqaO4SMjw62onOu6A==
+  dependencies:
+    "@types/d3-selection" "^2"
+
+"@types/d3-chord@^2":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-2.0.3.tgz#3009b792b754da964d893b4269d1fe7757f21370"
+  integrity sha512-koIqSNQLPRQPXt7c55hgRF6Lr9Ps72r1+Biv55jdYR+SHJ463MsB2lp4ktzttFNmrQw/9yWthf/OmSUj5dNXKw==
+
+"@types/d3-color@^2":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-2.0.3.tgz#8bc4589073c80e33d126345542f588056511fe82"
+  integrity sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==
+
+"@types/d3-contour@^2":
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-2.0.4.tgz#2fc5aa8949c1a1d12d183633603923025e3d14fd"
+  integrity sha512-WMac1xV/mXAgkgr5dUvzsBV5OrgNZDBDpJk9s3v2SadTqGgDRirKABb2Ek2H1pFlYVH4Oly9XJGnuzxKDduqWA==
+  dependencies:
+    "@types/d3-array" "^2"
+    "@types/geojson" "*"
+
+"@types/d3-delaunay@^5":
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-5.3.1.tgz#47ae03af6b78cb3aa39d3d3c42ca71daca488aef"
+  integrity sha512-F6itHi2DxdatHil1rJ2yEFUNhejj8+0Acd55LZ6Ggwbdoks0+DxVY2cawNj16sjCBiWvubVlh6eBMVsYRNGLew==
+
+"@types/d3-dispatch@^2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-2.0.1.tgz#d7dc50f9b679996ccf70f3c79dbbf99505a93107"
+  integrity sha512-eT2K8uG3rXkmRiCpPn0rNrekuSLdBfV83vbTvfZliA5K7dbeaqWS/CBHtJ9SQoF8aDTsWSY4A0RU67U/HcKdJQ==
+
+"@types/d3-drag@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-2.0.2.tgz#ed538d24456c839967a9ac7aab5e1b63b28bac7f"
+  integrity sha512-m9USoFaTgVw2mmE7vLjWTApT9dMxMlql/dl3Gj503x+1a2n6K455iDWydqy2dfCpkUBCoF82yRGDgcSk9FUEyQ==
+  dependencies:
+    "@types/d3-selection" "^2"
+
+"@types/d3-dsv@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-2.0.2.tgz#e10fa57576b50ded27e261db9984b9a92efec2f3"
+  integrity sha512-T4aL2ZzaILkLGKbxssipYVRs8334PSR9FQzTGftZbc3jIPGkiXXS7qUCh8/q8UWFzxBZQ92dvR0v7+AM9wL2PA==
+
+"@types/d3-ease@^2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-2.0.1.tgz#be03d29980ed7359be1d5b93ff666f95ddcbcf48"
+  integrity sha512-Af1ftZXv82ktPCk1+Vxe7f+VSfxDsQ1mwwakDl17+UzI/ii3vsDIAzaBDDSEQd2Cg9BYPTSx8wXH8rJNDuSjeg==
+
+"@types/d3-fetch@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-2.0.2.tgz#628c65d14b3a0d02fe1b9c2f3098b81a47e370bc"
+  integrity sha512-sllsCSWrNdSvzOJWN5RnxkmtvW9pCttONGajSxHX9FUQ9kOkGE391xlz6VDBdZxLnpwjp3I+mipbwsaCjq4m5A==
+  dependencies:
+    "@types/d3-dsv" "^2"
+
+"@types/d3-force@^2":
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-2.1.3.tgz#7b0d9ff608e394e6675cce5163eda8368fba7a07"
+  integrity sha512-b/1KrS7hESsMXZ3dOh5KrWPoDcQQbQKey344HO7F3o0tEcVzWHIgp1UMfHv8MLcysfHsRSPGpO7dRyLOVhMQnw==
+
+"@types/d3-format@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-2.0.2.tgz#97b2ac314430ae9f7768cc9efba8b23b63af82ef"
+  integrity sha512-OhQPuTeeMhD9A0Ksqo4q1S9Z1Q57O/t4tTPBxBQxRB4IERnxeoEYLPe72fA/GYpPSUrfKZVOgLHidkxwbzLdJA==
+
+"@types/d3-geo@^2":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-2.0.3.tgz#4af0f33c9e796aad6c3fc0dd8cadda9886d1fea9"
+  integrity sha512-kFwLEMXq1mGJ2Eho7KrOUYvLcc2YTDeKj+kTFt87JlEbRQ0rgo8ZENNb5vTYmZrJ2xL/vVM5M7yqVZGOPH2JFg==
+  dependencies:
+    "@types/geojson" "*"
+
+"@types/d3-hierarchy@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-2.0.2.tgz#afd09d509c36e8cd4907333556f8b591f23589e9"
+  integrity sha512-6PlBRwbjUPPt0ZFq/HTUyOAdOF3p73EUYots74lHMUyAVtdFSOS/hAeNXtEIM9i7qRDntuIblXxHGUMb9MuNRA==
+
+"@types/d3-interpolate@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-2.0.2.tgz#78eddf7278b19e48e8652603045528d46897aba0"
+  integrity sha512-lElyqlUfIPyWG/cD475vl6msPL4aMU7eJvx1//Q177L8mdXoVPFl1djIESF2FKnc0NyaHvQlJpWwKJYwAhUoCw==
+  dependencies:
+    "@types/d3-color" "^2"
+
+"@types/d3-path@^2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-2.0.1.tgz#ca03dfa8b94d8add97ad0cd97e96e2006b4763cb"
+  integrity sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw==
+
+"@types/d3-polygon@^2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-2.0.1.tgz#c2056594f85b512bc2b4f741caddd4b5448bc115"
+  integrity sha512-X3XTIwBxlzRIWe4yaD1KsmcfItjSPLTGL04QDyP08jyHDVsnz3+NZJMwtD4vCaTAVpGSjbqS+jrBo8cO2V/xMA==
+
+"@types/d3-quadtree@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-2.0.2.tgz#e3cd92b4e05318f98b0a16e780ba99ce7b13eb77"
+  integrity sha512-KgWL4jlz8QJJZX01E4HKXJ9FLU94RTuObsAYqsPp8YOAcYDmEgJIQJ+ojZcnKUAnrUb78ik8JBKWas5XZPqJnQ==
+
+"@types/d3-random@^2":
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-2.2.1.tgz#551edbb71cb317dea2cf9c76ebe059d311eefacb"
+  integrity sha512-5vvxn6//poNeOxt1ZwC7QU//dG9QqABjy1T7fP/xmFHY95GnaOw3yABf29hiu5SR1Oo34XcpyHFbzod+vemQjA==
+
+"@types/d3-scale-chromatic@^2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-2.0.1.tgz#495cbbae7273e0d0ff564cdc19aa6d2b9928da83"
+  integrity sha512-3EuZlbPu+pvclZcb1DhlymTWT2W+lYsRKBjvkH2ojDbCWDYavifqu1vYX9WGzlPgCgcS4Alhk1+zapXbGEGylQ==
+
+"@types/d3-scale@^3":
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-3.3.2.tgz#18c94e90f4f1c6b1ee14a70f14bfca2bd1c61d06"
+  integrity sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==
+  dependencies:
+    "@types/d3-time" "^2"
+
+"@types/d3-selection@^2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-2.0.1.tgz#bc2816c96faff285d204dda72b79734d4f37d583"
+  integrity sha512-3mhtPnGE+c71rl/T5HMy+ykg7migAZ4T6gzU0HxpgBFKcasBrSnwRbYV1/UZR6o5fkpySxhWxAhd7yhjj8jL7g==
+
+"@types/d3-shape@^2":
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-2.1.3.tgz#35d397b9e687abaa0de82343b250b9897b8cacf3"
+  integrity sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ==
+  dependencies:
+    "@types/d3-path" "^2"
+
+"@types/d3-time-format@^3":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-3.0.1.tgz#1680fb6c41ab3a85db261ede296626668592246a"
+  integrity sha512-5GIimz5IqaRsdnxs4YlyTZPwAMfALu/wA4jqSiuqgdbCxUZ2WjrnwANqOtoBJQgeaUTdYNfALJO0Yb0YrDqduA==
+
+"@types/d3-time@^2":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-2.1.1.tgz#743fdc821c81f86537cbfece07093ac39b4bc342"
+  integrity sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==
+
+"@types/d3-timer@^2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-2.0.1.tgz#ffb6620d290624f3726aa362c0c8a4b44c8d7200"
+  integrity sha512-TF8aoF5cHcLO7W7403blM7L1T+6NF3XMyN3fxyUolq2uOcFeicG/khQg/dGxiCJWoAcmYulYN7LYSRKO54IXaA==
+
+"@types/d3-transition@^2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-2.0.2.tgz#d5ba1c26a3daeb0c5527d573d44b4c5ca9fae027"
+  integrity sha512-376TICEykdXOEA9uUIYpjshEkxfGwCPnkHUl8+6gphzKbf5NMnUhKT7wR59Yxrd9wtJ/rmE3SVLx6/8w4eY6Zg==
+  dependencies:
+    "@types/d3-selection" "^2"
+
+"@types/d3-zoom@^2":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-2.0.3.tgz#9eef8763600fa8be11b8cb0ed9144a395df6dffb"
+  integrity sha512-9X9uDYKk2U8w775OHj36s9Q7GkNAnJKGw6+sbkP5DpHSjELwKvTGzEK6+IISYfLpJRL/V3mRXMhgDnnJ5LkwJg==
+  dependencies:
+    "@types/d3-interpolate" "^2"
+    "@types/d3-selection" "^2"
+
+"@types/d3@^6.7.0":
+  version "6.7.5"
+  resolved "https://registry.yarnpkg.com/@types/d3/-/d3-6.7.5.tgz#6ae8034ea21db10fa3e31db1f670c5887d91d8a3"
+  integrity sha512-TUZ6zuT/KIvbHSv81kwAiO5gG5aTuoiLGnWR/KxHJ15Idy/xmGUXaaF5zMG+UMIsndcGlSHTmrvwRgdvZlNKaA==
+  dependencies:
+    "@types/d3-array" "^2"
+    "@types/d3-axis" "^2"
+    "@types/d3-brush" "^2"
+    "@types/d3-chord" "^2"
+    "@types/d3-color" "^2"
+    "@types/d3-contour" "^2"
+    "@types/d3-delaunay" "^5"
+    "@types/d3-dispatch" "^2"
+    "@types/d3-drag" "^2"
+    "@types/d3-dsv" "^2"
+    "@types/d3-ease" "^2"
+    "@types/d3-fetch" "^2"
+    "@types/d3-force" "^2"
+    "@types/d3-format" "^2"
+    "@types/d3-geo" "^2"
+    "@types/d3-hierarchy" "^2"
+    "@types/d3-interpolate" "^2"
+    "@types/d3-path" "^2"
+    "@types/d3-polygon" "^2"
+    "@types/d3-quadtree" "^2"
+    "@types/d3-random" "^2"
+    "@types/d3-scale" "^3"
+    "@types/d3-scale-chromatic" "^2"
+    "@types/d3-selection" "^2"
+    "@types/d3-shape" "^2"
+    "@types/d3-time" "^2"
+    "@types/d3-time-format" "^3"
+    "@types/d3-timer" "^2"
+    "@types/d3-transition" "^2"
+    "@types/d3-zoom" "^2"
+
 "@types/fbemitter@*":
   version "2.0.32"
   resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.1.tgz#24691fa2b0c3ec8c0d34bfcfd495edac5593ebb4"
   integrity sha512-N87VuQi7HEeRJkhzovao/JviiqKjDKMVKxKMfUvSKw+MbkbW8R0nA3fi/MQhhlxV2fQ+2ReM+/Nt4efdrJx3zA==
 
-"@types/node@12.0.0":
-  version "12.0.0"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.0.tgz#d11813b9c0ff8aaca29f04cbc12817f4c7d656e5"
-  integrity sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg==
+"@types/node@^12.0.0":
+  version "12.20.16"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.16.tgz#1acf34f6456208f495dac0434dd540488d17f991"
+  integrity sha512-6CLxw83vQf6DKqXxMPwl8qpF8I7THFZuIwLt4TnNsumxkp1VsRZWT8txQxncT/Rl2UojTsFzWgDG4FRMwafrlA==
 
 "@types/normalize-package-data@^2.4.0":
   version "2.4.1"
@@ -3610,6 +3820,11 @@ commander@2.17.x:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
   integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
 
+commander@7:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+  integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
 commander@^2.12.1, commander@^2.19.0, commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -4045,6 +4260,250 @@ cyclist@^1.0.1:
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
   integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
 
+"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.0.1.tgz#ca45c263f5bb780ab5a34a6e1d3d5883fe4a8d14"
+  integrity sha512-l3Bh5o8RSoC3SBm5ix6ogaFW+J6rOUm42yOtZ2sQPCEvCqUMepeX7zgrlLLGIemxgOyo9s2CsWEidnLv5PwwRw==
+  dependencies:
+    internmap "1 - 2"
+
+d3-axis@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322"
+  integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==
+
+d3-brush@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c"
+  integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
+  dependencies:
+    d3-dispatch "1 - 3"
+    d3-drag "2 - 3"
+    d3-interpolate "1 - 3"
+    d3-selection "3"
+    d3-transition "3"
+
+d3-chord@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966"
+  integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==
+  dependencies:
+    d3-path "1 - 3"
+
+"d3-color@1 - 3", d3-color@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a"
+  integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==
+
+d3-contour@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-3.0.1.tgz#2c64255d43059599cd0dba8fe4cc3d51ccdd9bbd"
+  integrity sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==
+  dependencies:
+    d3-array "2 - 3"
+
+d3-delaunay@6:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92"
+  integrity sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==
+  dependencies:
+    delaunator "5"
+
+"d3-dispatch@1 - 3", d3-dispatch@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
+  integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
+
+"d3-drag@2 - 3", d3-drag@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
+  integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
+  dependencies:
+    d3-dispatch "1 - 3"
+    d3-selection "3"
+
+"d3-dsv@1 - 3", d3-dsv@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
+  integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
+  dependencies:
+    commander "7"
+    iconv-lite "0.6"
+    rw "1"
+
+"d3-ease@1 - 3", d3-ease@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
+  integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
+
+d3-fetch@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22"
+  integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==
+  dependencies:
+    d3-dsv "1 - 3"
+
+d3-force@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4"
+  integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==
+  dependencies:
+    d3-dispatch "1 - 3"
+    d3-quadtree "1 - 3"
+    d3-timer "1 - 3"
+
+"d3-format@1 - 3", d3-format@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.0.1.tgz#e41b81b2ab79277141ec1404aa5d05001da64084"
+  integrity sha512-hdL7+HBIohpgfolhBxr1KX47VMD6+vVD/oEFrxk5yhmzV2prk99EkFKYpXuhVkFpTgHdJ6/4bYcjdLPPXV4tIA==
+
+d3-geo@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.0.1.tgz#4f92362fd8685d93e3b1fae0fd97dc8980b1ed7e"
+  integrity sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==
+  dependencies:
+    d3-array "2.5.0 - 3"
+
+d3-hierarchy@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.0.1.tgz#0365342d54972e38ca05e9143e0ab1c60846b3b5"
+  integrity sha512-RlLTaofEoOrMK1JoXYIGhKTkJFI/6rFrYPgxy6QlZo2BcVc4HGTqEU0rPpzuMq5T/5XcMtAzv1XiLA3zRTfygw==
+
+"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
+  integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
+  dependencies:
+    d3-color "1 - 3"
+
+"d3-path@1 - 3", d3-path@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e"
+  integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==
+
+d3-polygon@3, d3-polygon@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398"
+  integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==
+
+"d3-quadtree@1 - 3", d3-quadtree@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f"
+  integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==
+
+d3-random@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4"
+  integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==
+
+d3-scale-chromatic@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a"
+  integrity sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==
+  dependencies:
+    d3-color "1 - 3"
+    d3-interpolate "1 - 3"
+
+d3-scale@4:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.0.tgz#294377ea1d7e5a31509ee648b98d7916ac0b34e3"
+  integrity sha512-foHQYKpWQcyndH1CGoHdUC4PECxTxonzwwBXGT8qu+Drb1FIc6ON6dG2P5f4hRRMkLiIKeWK7iFtdznDUrnuPQ==
+  dependencies:
+    d3-array "2.10.0 - 3"
+    d3-format "1 - 3"
+    d3-interpolate "1.2.0 - 3"
+    d3-time "2.1.1 - 3"
+    d3-time-format "2 - 4"
+
+"d3-selection@2 - 3", d3-selection@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
+  integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
+
+d3-shape@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.0.1.tgz#9ccdfb28fd9b0d12f2d8aec234cd5c4a9ea27931"
+  integrity sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw==
+  dependencies:
+    d3-path "1 - 3"
+
+"d3-time-format@2 - 4", d3-time-format@4:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.0.0.tgz#930ded86a9de761702344760d8a25753467f28b7"
+  integrity sha512-nzaCwlj+ZVBIlFuVOT1RmU+6xb/7D5IcnhHzHQcBgS/aTa5K9fWZNN5LCXA27LgF5WxoSNJqKBbLcGMtM6Ca6A==
+  dependencies:
+    d3-time "1 - 3"
+
+"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975"
+  integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==
+  dependencies:
+    d3-array "2 - 3"
+
+"d3-timer@1 - 3", d3-timer@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
+  integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
+
+"d3-transition@2 - 3", d3-transition@3:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
+  integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
+  dependencies:
+    d3-color "1 - 3"
+    d3-dispatch "1 - 3"
+    d3-ease "1 - 3"
+    d3-interpolate "1 - 3"
+    d3-timer "1 - 3"
+
+d3-zoom@3:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
+  integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
+  dependencies:
+    d3-dispatch "1 - 3"
+    d3-drag "2 - 3"
+    d3-interpolate "1 - 3"
+    d3-selection "2 - 3"
+    d3-transition "2 - 3"
+
+d3@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/d3/-/d3-7.0.0.tgz#fe6036b38ba2026ff34223e208fd294db1b997da"
+  integrity sha512-t+jEKGO2jQiSBLJYYq6RFc500tsCeXBB4x41oQaSnZD3Som95nQrlw9XJGrFTMUOQOkwSMauWy9+8Tz1qm9UZw==
+  dependencies:
+    d3-array "3"
+    d3-axis "3"
+    d3-brush "3"
+    d3-chord "3"
+    d3-color "3"
+    d3-contour "3"
+    d3-delaunay "6"
+    d3-dispatch "3"
+    d3-drag "3"
+    d3-dsv "3"
+    d3-ease "3"
+    d3-fetch "3"
+    d3-force "3"
+    d3-format "3"
+    d3-geo "3"
+    d3-hierarchy "3"
+    d3-interpolate "3"
+    d3-path "3"
+    d3-polygon "3"
+    d3-quadtree "3"
+    d3-random "3"
+    d3-scale "4"
+    d3-scale-chromatic "3"
+    d3-selection "3"
+    d3-shape "3"
+    d3-time "3"
+    d3-time-format "4"
+    d3-timer "3"
+    d3-transition "3"
+    d3-zoom "3"
+
 dargs@^4.0.1:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17"
@@ -4221,6 +4680,13 @@ del@^3.0.0:
     pify "^3.0.0"
     rimraf "^2.2.8"
 
+delaunator@5:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.0.tgz#60f052b28bd91c9b4566850ebf7756efe821d81b"
+  integrity sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==
+  dependencies:
+    robust-predicates "^3.0.0"
+
 delayed-stream@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@@ -5944,7 +6410,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
   dependencies:
     safer-buffer ">= 2.1.2 < 3"
 
-iconv-lite@^0.6.2:
+iconv-lite@0.6, iconv-lite@^0.6.2:
   version "0.6.3"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
   integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
@@ -6134,6 +6600,11 @@ internal-ip@^3.0.1:
     default-gateway "^2.6.0"
     ipaddr.js "^1.5.2"
 
+"internmap@1 - 2":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.1.tgz#33d0fa016185397549fb1a14ea3dbe5a2949d1cd"
+  integrity sha512-Ujwccrj9FkGqjbY3iVoxD1VV+KdZZeENx0rphrtzmRXbFvkFO88L80BL/zeSIguX/7T+y8k04xqtgWgS5vxwxw==
+
 interpret@^1.1.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
@@ -10020,6 +10491,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
     hash-base "^3.0.0"
     inherits "^2.0.1"
 
+robust-predicates@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a"
+  integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==
+
 rsvp@^3.3.3:
   version "3.6.2"
   resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
@@ -10037,7 +10513,7 @@ run-queue@^1.0.0, run-queue@^1.0.3:
   dependencies:
     aproba "^1.1.1"
 
-rw@^1.3.3:
+rw@1, rw@^1.3.3:
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
   integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=