Unverified Commit 52a0a3c3 authored by Kevin Di Lallo's avatar Kevin Di Lallo Committed by GitHub
Browse files

Merge pull request #98 from dilallkx/kd_sp23_dev_cfg_map

Geospatial Data Support - Frontend
parents d0cdae56 c2952931
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
{
  "styles": {
    "standard": [
      "dark-matter",
      "klokantech-basic",
      "osm-bright",
      "positron"
    ],
    "custom": [],
    "lang": "",
    "langLatin": true,
    "langAlts": true
  },
  "settings": {
    "serve": {
      "vector": true,
      "raster": true,
      "services": true,
      "static": true
    },
    "raster": {
      "format": "PNG_256",
      "hidpi": 2,
      "maxsize": 2048
    },
    "server": {
      "title": "",
      "redirect": "",
      "domains": []
    },
    "memcache": {
      "size": 56.5,
      "servers": [
        "localhost:11211"
      ]
    }
  }
}
 No newline at end of file
+92 −0
Original line number Diff line number Diff line
@@ -927,6 +927,17 @@
        "minimist": "^1.2.0"
      }
    },
    "@geoman-io/leaflet-geoman-free": {
      "version": "2.5.0",
      "resolved": "https://registry.npmjs.org/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.5.0.tgz",
      "integrity": "sha512-Zq/gOGPpsRurTrW3d3qiXlvEPHZfU/PZ7jr02jtweaN0kXyHo/WqkihMS9h5yUySjJBXdUC9Xuuo4/ZzHqEH4g==",
      "requires": {
        "@turf/difference": "^6.0.2",
        "@turf/intersect": "^6.1.3",
        "@turf/kinks": "6.x",
        "lodash": "^4.17.15"
      }
    },
    "@jest/console": {
      "version": "24.9.0",
      "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz",
@@ -3773,6 +3784,66 @@
        }
      }
    },
    "@turf/area": {
      "version": "6.0.1",
      "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.0.1.tgz",
      "integrity": "sha512-Zv+3N1ep9P5JvR0YOYagLANyapGWQBh8atdeR3bKpWcigVXFsEKNUw03U/5xnh+cKzm7yozHD6MFJkqQv55y0g==",
      "requires": {
        "@turf/helpers": "6.x",
        "@turf/meta": "6.x"
      }
    },
    "@turf/difference": {
      "version": "6.0.2",
      "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-6.0.2.tgz",
      "integrity": "sha512-WtXkvFgOyHqsG3xtYG/m5Su+gkvyCUTbdW0XOuc3Ha2u9UeeBSGwEzTc2y9THDLDhHqR+DlTl1MMEBihXcy3fg==",
      "requires": {
        "@turf/area": "6.x",
        "@turf/helpers": "6.x",
        "@turf/invariant": "6.x",
        "@turf/meta": "6.x",
        "martinez-polygon-clipping": "^0.4.3"
      }
    },
    "@turf/helpers": {
      "version": "6.1.4",
      "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.1.4.tgz",
      "integrity": "sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g=="
    },
    "@turf/intersect": {
      "version": "6.1.3",
      "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-6.1.3.tgz",
      "integrity": "sha512-SeAJG/zPRRTeyK2OifkPoyLq60q8tv8prpPIH3R8ZhyF4MdLOnMv5MURaQ6kQd+3UTDrL+pYm6rqbPvln1zqIw==",
      "requires": {
        "@turf/helpers": "6.x",
        "@turf/invariant": "6.x",
        "martinez-polygon-clipping": "^0.4.3"
      }
    },
    "@turf/invariant": {
      "version": "6.1.2",
      "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.1.2.tgz",
      "integrity": "sha512-WU08Ph8j0J2jVGlQCKChXoCtI50BB3yEH21V++V0T4cR1T27HKCxkehV2sYMwTierfMBgjwSwDIsxnR4/2mWXg==",
      "requires": {
        "@turf/helpers": "6.x"
      }
    },
    "@turf/kinks": {
      "version": "6.0.0",
      "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-6.0.0.tgz",
      "integrity": "sha512-X2xk3+Sr9aeu51t1vBa7Iwowvjxcbl5fePLIo/dDOU4j5WeFoqGCto+DdEHQcC3y8HVUin4i8GaHVaM9sVTphg==",
      "requires": {
        "@turf/helpers": "6.x"
      }
    },
    "@turf/meta": {
      "version": "6.0.2",
      "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.0.2.tgz",
      "integrity": "sha512-VA7HJkx7qF1l3+GNGkDVn2oXy4+QoLP6LktXAaZKjuT1JI0YESat7quUkbCMy4zP9lAUuvS4YMslLyTtr919FA==",
      "requires": {
        "@turf/helpers": "6.x"
      }
    },
    "@types/babel__core": {
      "version": "7.1.3",
      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz",
@@ -10847,6 +10918,22 @@
      "resolved": "https://registry.npmjs.org/mapbox-gl-leaflet/-/mapbox-gl-leaflet-0.0.12.tgz",
      "integrity": "sha512-bfGl+CdAYc51ua8s9OOq5J0EcAnHuxS0OoQP5EWsVmSUGFvKkgOD+Od2sFmDOl+zBUjdBEvqLaf9t/tf2BtrWw=="
    },
    "martinez-polygon-clipping": {
      "version": "0.4.3",
      "resolved": "https://registry.npmjs.org/martinez-polygon-clipping/-/martinez-polygon-clipping-0.4.3.tgz",
      "integrity": "sha512-3ZNS0ksKhWTLsmCUkNf+/UimndZ5U2cVOS0I+IjiwF+M23E77TmeOZSmbRJbfCoQUog/vcQ42s3DXrhgOhgPqw==",
      "requires": {
        "splaytree": "^0.1.4",
        "tinyqueue": "^1.2.0"
      },
      "dependencies": {
        "tinyqueue": {
          "version": "1.2.3",
          "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-1.2.3.tgz",
          "integrity": "sha512-Qz9RgWuO9l8lT+Y9xvbzhPT2efIUIFd69N7eF7tJ9lnQl0iLj1M7peK7IoUGZL9DJHw9XftqLreccfxcQgYLxA=="
        }
      }
    },
    "material-components-web": {
      "version": "0.38.1",
      "resolved": "https://registry.npmjs.org/material-components-web/-/material-components-web-0.38.1.tgz",
@@ -15151,6 +15238,11 @@
      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
      "dev": true
    },
    "splaytree": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-0.1.4.tgz",
      "integrity": "sha512-D50hKrjZgBzqD3FT2Ek53f2dcDLAQT8SSGrzj3vidNH5ISRgceeGVJ2dQIthKOuayqFXfFjXheHNo4bbt9LhRQ=="
    },
    "split-string": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+1 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
    "webpack-serve": "^0.3.1"
  },
  "dependencies": {
    "@geoman-io/leaflet-geoman-free": "2.5.0",
    "@material-ui/core": "^1.5.1",
    "@material-ui/icons": "^1.1.1",
    "axios": "^0.18.0",
+199 −19
Original line number Diff line number Diff line
@@ -20,12 +20,13 @@ import React, { Component } from 'react';
import { Select } from '@rmwc/select';
import { Grid, GridCell, GridInner } from '@rmwc/grid';
import { Button } from '@rmwc/button';
import { TextField, TextFieldHelperText } from '@rmwc/textfield';
import { TextField, TextFieldIcon, TextFieldHelperText } from '@rmwc/textfield';
import { Checkbox } from '@rmwc/checkbox';
import { Typography } from '@rmwc/typography';

import { updateObject } from '../../util/object-util';
import { createUniqueName } from '../../util/elem-utils';
import L from 'leaflet';

import IDSelect from '../../components/helper-components/id-select';
import CancelApplyPair from '../../components/helper-components/cancel-apply-pair';
@@ -52,6 +53,11 @@ import {
  FIELD_MNC,
  FIELD_DEFAULT_CELL_ID,
  FIELD_CELL_ID,
  FIELD_GEO_LOCATION,
  FIELD_GEO_RADIUS,
  FIELD_GEO_PATH,
  FIELD_GEO_EOP_MODE,
  FIELD_GEO_VELOCITY,
  FIELD_CHART_ENABLED,
  FIELD_CHART_LOC,
  FIELD_CHART_VAL,
@@ -123,6 +129,11 @@ import {
  CFG_ELEM_MCC,
  CFG_ELEM_DEFAULT_CELL_ID,
  CFG_ELEM_CELL_ID,
  CFG_ELEM_GEO_LOCATION,
  CFG_ELEM_GEO_RADIUS,
  CFG_ELEM_GEO_PATH,
  CFG_ELEM_GEO_EOP_MODE,
  CFG_ELEM_GEO_VELOCITY,
  CFG_ELEM_CHART_CHECK,
  CFG_ELEM_CHART_LOC,
  CFG_ELEM_CHART_GROUP,
@@ -134,7 +145,9 @@ import {
  CFG_BTN_CLONE_ELEM,

  // Layout type
  MEEP_COMPONENT_TABLE_LAYOUT
  MEEP_COMPONENT_TABLE_LAYOUT,
  GEO_EOP_MODE_LOOP,
  GEO_EOP_MODE_REVERSE
} from '../../meep-constants';

// ELEMENT VALIDATION
@@ -145,6 +158,7 @@ const SERVICE_NODE_PORT_MIN = 30000;
const SERVICE_NODE_PORT_MAX = 32767;
const GPU_COUNT_MIN = 1;
const GPU_COUNT_MAX = 4;
const EOP_MODES = [GEO_EOP_MODE_LOOP, GEO_EOP_MODE_REVERSE];

const validateName = val => {
  if (val) {
@@ -215,6 +229,14 @@ const validateInt = val => {
  return val.indexOf('.') === -1 ? null : 'Must be an integer';
};

const validatePositiveInt = val => {
  const intError = validateInt(val);
  if (intError) {
    return intError;
  }
  return val >= 0 ? null : 'Must be a positive integer';
};

const validatePath = val => {
  /*eslint-disable */
  if (val.match(/^.*?(?=[\^#%&$\*<>\?\{\|\} ]).*$/)) {
@@ -280,6 +302,29 @@ const validateCellularCellId = val => {
  return null;
};

const validateLocation = val => {
  if (val) {
    try {
      L.GeoJSON.coordsToLatLng(JSON.parse(val));
    } catch(e) {
      return '[longitude,latitude]';
    }
  }
  return null;
};

const validateGeoPath = val => {
  if (val) {
    // TODO -- Validate location format
    try {
      L.GeoJSON.coordsToLatLngs(JSON.parse(val),0);
    } catch(e) {
      return '[[longitude,latitude],...]';
    }
  }
  return null;
};

const validateExternalPort = port => {
  if (port === '') {
    return null;
@@ -409,8 +454,15 @@ const CfgTextField = props => {
    <>
      <TextField
        outlined
        style={{ width: '100%' }}
        style={{ width: '100%', marginBottom: 0 }}
        label={props.label}
        withLeadingIcon={!props.icon ? null : 
          <TextFieldIcon
            tabIndex="0"
            icon={props.icon}
            onClick={props.onIconClick}
          />
        }
        type={props.type}
        onChange={event => {
          var err = props.validate ? props.validate(event.target.value) : null;
@@ -610,10 +662,11 @@ const UserChartFields = ({ element, onUpdate }) => {
};

// Display element-specific form fields
const TypeRelatedFormFields = ({ onUpdate, element }) => {
const TypeRelatedFormFields = ({ onUpdate, onEditLocation, onEditPath, element }) => {
  var type = getElemFieldVal(element, FIELD_TYPE);
  var isExternal = getElemFieldVal(element, FIELD_IS_EXTERNAL);
  var chartEnabled = getElemFieldVal(element, FIELD_CHART_ENABLED);
  var eopMode = getElemFieldVal(element, FIELD_GEO_EOP_MODE) || '';

  switch (type) {
  case ELEMENT_TYPE_SCENARIO:
@@ -683,11 +736,36 @@ const TypeRelatedFormFields = ({ onUpdate, element }) => {
    );
  case ELEMENT_TYPE_POA:
    return (
      <>
        <NCGroups
          onUpdate={onUpdate}
          element={element}
          prefixes={[PREFIX_TERM_LINK]}
        />
        <Grid>
          <CfgTextFieldCell
            span={8}
            icon='location_on'
            onIconClick={onEditLocation}
            onUpdate={onUpdate}
            element={element}
            validate={validateLocation}
            label='Location Coordinates'
            fieldName={FIELD_GEO_LOCATION}
            cydata={CFG_ELEM_GEO_LOCATION}
          />
          <CfgTextFieldCell
            span={4}
            onUpdate={onUpdate}
            element={element}
            isNumber={true}
            label='Radius (m)'
            validate={validatePositiveInt}
            fieldName={FIELD_GEO_RADIUS}
            cydata={CFG_ELEM_GEO_RADIUS}
          />
        </Grid>
      </>
    );
  case ELEMENT_TYPE_POA_CELL:
    return (
@@ -697,6 +775,29 @@ const TypeRelatedFormFields = ({ onUpdate, element }) => {
          element={element}
          prefixes={[PREFIX_TERM_LINK]}
        />
        <Grid>
          <CfgTextFieldCell
            span={8}
            icon='location_on'
            onIconClick={onEditLocation}
            onUpdate={onUpdate}
            element={element}
            validate={validateLocation}
            label='Location Coordinates'
            fieldName={FIELD_GEO_LOCATION}
            cydata={CFG_ELEM_GEO_LOCATION}
          />
          <CfgTextFieldCell
            span={4}
            onUpdate={onUpdate}
            element={element}
            isNumber={true}
            label='Radius (m)'
            validate={validateNumber}
            fieldName={FIELD_GEO_RADIUS}
            cydata={CFG_ELEM_GEO_RADIUS}
          />
        </Grid>
        <CfgTextFieldCell
          onUpdate={onUpdate}
          element={element}
@@ -708,15 +809,84 @@ const TypeRelatedFormFields = ({ onUpdate, element }) => {
      </>
    );
  case ELEMENT_TYPE_UE:
    return (
      <>
        <NCGroups
          onUpdate={onUpdate}
          element={element}
          prefixes={[PREFIX_LINK]}
        />
        <Grid>
          <CfgTextFieldCell
            span={12}
            icon='location_on'
            onIconClick={onEditLocation}
            onUpdate={onUpdate}
            element={element}
            validate={validateLocation}
            label='Location Coordinates'
            fieldName={FIELD_GEO_LOCATION}
            cydata={CFG_ELEM_GEO_LOCATION}
          />
          <CfgTextFieldCell
            span={12}
            icon='location_on'
            onIconClick={onEditPath}
            onUpdate={onUpdate}
            element={element}
            validate={validateGeoPath}
            label='Path Coordinates'
            fieldName={FIELD_GEO_PATH}
            cydata={CFG_ELEM_GEO_PATH}
          />
          <GridCell span={6} style={{ paddingTop: 16 }}>
            <IDSelect
              label='End-of-Path Mode'
              span={12}
              options={EOP_MODES}
              onChange={elem => onUpdate(FIELD_GEO_EOP_MODE, elem.target.value, null)}
              value={eopMode}
              disabled={false}
              cydata={CFG_ELEM_GEO_EOP_MODE}
            />
          </GridCell>
          <CfgTextFieldCell
            span={6}
            onUpdate={onUpdate}
            element={element}
            validate={validateNumber}
            isNumber={true}
            label='Velocity (m/s)'
            fieldName={FIELD_GEO_VELOCITY}
            cydata={CFG_ELEM_GEO_VELOCITY}
          />
        </Grid>
      </>
    );
  case ELEMENT_TYPE_DC:
  case ELEMENT_TYPE_EDGE:
  case ELEMENT_TYPE_FOG:
    return (
      <>
        <NCGroups
          onUpdate={onUpdate}
          element={element}
          prefixes={[PREFIX_LINK]}
        />
        <Grid>
          <CfgTextFieldCell
            span={12}
            icon='location_on'
            onIconClick={onEditLocation}
            onUpdate={onUpdate}
            element={element}
            validate={validateLocation}
            label='Location Coordinates'
            fieldName={FIELD_GEO_LOCATION}
            cydata={CFG_ELEM_GEO_LOCATION}
          />
        </Grid>
      </>
    );
  case ELEMENT_TYPE_UE_APP:
    return (
@@ -1236,6 +1406,16 @@ export class CfgNetworkElementContainer extends Component {
    this.props.cfgElemUpdate(elem);
  }

  onEditLocation() {
    var elem = updateObject({}, this.props.configuredElement);
    this.props.onEditLocation(elem);
  }

  onEditPath() {
    var elem = updateObject({}, this.props.configuredElement);
    this.props.onEditPath(elem);
  }

  render() {
    const element = this.props.configuredElement;
    return (
@@ -1282,9 +1462,9 @@ export class CfgNetworkElementContainer extends Component {

            <TypeRelatedFormFields
              element={element}
              onUpdate={(name, val, err) => {
                this.onUpdateElement(name, val, err);
              }}
              onUpdate={(name, val, err) => this.onUpdateElement(name, val, err)}
              onEditLocation={() => this.onEditLocation()}
              onEditPath={() => this.onEditPath()}
            />

            <div
@@ -1315,10 +1495,10 @@ const styles = {
    height: '100%'
  },
  block: {
    marginBottom: 10
    marginBottom: 0
  },
  field: {
    marginBottom: 10
    marginBottom: 0
  },
  button: {
    color: 'white'
+67 −19
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import React, { Component } from 'react';
import * as YAML from 'yamljs';
import { Grid, GridCell, GridInner } from '@rmwc/grid';
import { Elevation } from '@rmwc/elevation';
import IDSelect from '../../components/helper-components/id-select';
import IDCMap from '../idc-map';
import IDCVis from '../idc-vis';
import CfgNetworkElementContainer from './cfg-network-element-container';
import CfgPageScenarioButtons from './cfg-page-scenario-buttons';
@@ -45,7 +47,8 @@ import {
} from '../../state/cfg';

import {
  uiChangeCurrentDialog
  uiChangeCurrentDialog,
  uiCfgChangeView
} from '../../state/ui';

import {
@@ -53,6 +56,9 @@ import {
  CFG_STATE_LOADED,
  CFG_STATE_NEW,
  CFG_STATE_IDLE,
  CFG_VIEW_NETWORK,
  CFG_VIEW_MAP,
  CFG_VIEW_TYPE,
  PAGE_CONFIGURE,
  ELEMENT_TYPE_SCENARIO,
  IDC_DIALOG_OPEN_SCENARIO,
@@ -115,7 +121,9 @@ class CfgPageContainer extends Component {
  // EDIT
  onEditElement(element) {
    if (element !== null) {
      if (!this.props.configuredElement || (element.id !== this.props.configuredElement.id)) {
        this.props.cfgElemEdit(element);
      }
    } else {
      this.props.cfgElemClear();
    }
@@ -129,10 +137,7 @@ class CfgPageContainer extends Component {
    }

    // Add/update element in scenario
    if (
      this.props.cfg.elementConfiguration.configurationMode ===
      CFG_ELEM_MODE_NEW
    ) {
    if (this.props.cfg.elementConfiguration.configurationMode === CFG_ELEM_MODE_NEW) {
      this.props.newScenarioElem(element, true);
    } else {
      this.props.updateScenarioElem(element);
@@ -173,6 +178,24 @@ class CfgPageContainer extends Component {
    this.props.cfgElemClear();
  }

  // Edit Location
  onEditLocation() {
    this.toggleCfgView();
  }

  // Edit Path
  onEditPath() {
    this.toggleCfgView();
  }

  toggleCfgView() {
    if (this.props.cfgView === CFG_VIEW_NETWORK) {
      this.props.changeView(CFG_VIEW_MAP);
    } else {
      this.props.changeView(CFG_VIEW_NETWORK);
    }
  }

  findIndexByKeyValue(_array, key, value) {
    for (var i = 0; i < _array.length; i++) {
      if (getElemFieldVal(_array[i], key) === value) {
@@ -585,13 +608,26 @@ class CfgPageContainer extends Component {
                style={styles.headline}
              >
                <GridInner>
                  <GridCell align={'middle'} span={4}>
                  <IDSelect
                    label="View"
                    span={2}
                    options={[CFG_VIEW_NETWORK, CFG_VIEW_MAP]}
                    onChange={(e) => this.props.changeView(e.target.value)}
                    value={this.props.cfgView}
                    disabled={false}
                    cydata={CFG_VIEW_TYPE}
                  />
                  <GridCell align={'middle'} style={{ height: '100%'}} span={3}>
                    <GridInner style={{ marginLeft: 10, height: '100%', borderLeft: '2px solid #e4e4e4'}}>
                      <GridCell align={'middle'} style={{ marginLeft: 20}} span={12}>
                        <HeadlineBar
                          titleLabel="Scenario"
                          scenarioName={this.props.scenarioName}
                        />
                      </GridCell>
                  <GridCell align={'middle'} span={8}>
                    </GridInner>
                  </GridCell>
                  <GridCell align={'middle'} span={7}>
                    <GridInner align={'right'}>
                      <GridCell span={12}>
                        <CfgPageScenarioButtons
@@ -629,12 +665,20 @@ class CfgPageContainer extends Component {
                <GridCell span={8}>
                  <Elevation className="component-style" z={2}>
                    <div style={{ padding: 10, height: '70vh' }}>
                      {this.props.cfgView === CFG_VIEW_NETWORK && (
                        <IDCVis
                          type={TYPE_CFG}
                          width='100%'
                          height='100%'
                          onEditElement={elem => this.onEditElement(elem)}
                        />
                      )}
                      {this.props.cfgView === CFG_VIEW_MAP && (
                        <IDCMap
                          type={TYPE_CFG}
                          onEditElement={elem => this.onEditElement(elem)}
                        />
                      )}
                    </div>
                  </Elevation>
                </GridCell>
@@ -647,6 +691,8 @@ class CfgPageContainer extends Component {
                      onDeleteElement={elem => this.onDeleteElement(elem)}
                      onApplyCloneElement={elem => this.onApplyCloneElement(elem)}
                      onCancelElement={() => this.onCancelElement()}
                      onEditLocation={elem => this.onEditLocation(elem)}
                      onEditPath={elem => this.onEditPath(elem)}
                    />
                  </Elevation>
                </GridCell>
@@ -695,6 +741,7 @@ const mapStateToProps = state => {
  return {
    cfg: state.cfg,
    cfgState: state.cfg.state,
    cfgView: state.ui.cfgView,
    configuredElement: state.cfg.elementConfiguration.configuredElement,
    table: state.cfg.table,
    selectedElements: state.cfg.table.selected,
@@ -715,7 +762,8 @@ const mapDispatchToProps = dispatch => {
    changeCurrentDialog: type => dispatch(uiChangeCurrentDialog(type)),
    changeScenarioList: scenarios => dispatch(cfgChangeScenarioList(scenarios)),
    changeState: s => dispatch(cfgChangeState(s)),
    changeScenario: scenario => dispatch(cfgChangeScenario(scenario))
    changeScenario: scenario => dispatch(cfgChangeScenario(scenario)),
    changeView: view => dispatch(uiCfgChangeView(view))
  };
};

Loading