summaryrefslogtreecommitdiffstats
path: root/src/generic-components/map/TopographicMap.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/generic-components/map/TopographicMap.jsx')
-rw-r--r--src/generic-components/map/TopographicMap.jsx260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/generic-components/map/TopographicMap.jsx b/src/generic-components/map/TopographicMap.jsx
new file mode 100644
index 0000000..d4e91d6
--- /dev/null
+++ b/src/generic-components/map/TopographicMap.jsx
@@ -0,0 +1,260 @@
+/*
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * 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=====================================================
+ *
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
+ */
+
+import React, {Component, PropTypes} from 'react';
+import {geoAlbersUsa, geoEquirectangular, geoPath} from 'd3-geo';
+import {feature, mesh} from 'topojson';
+
+import {
+ BOUNDARY_MESH_KEY,
+ LAND_FEATURE_KEY,
+ MAP_OBJECT_KEYS,
+ PLOT_POINT_KEY_BASE,
+ PLOT_POINT_SHAPES,
+ PROJECTION_TYPES
+} from './MapConstants.js';
+import usMapJson from './mapJson/usJson.json';
+import worldMapJson from './mapJson/worldJson.json';
+
+class TopographicMap extends Component {
+ static propTypes = {
+ width: PropTypes.number,
+ height: PropTypes.number,
+ pointArray: PropTypes.array,
+ currentProjection: PropTypes.string
+ };
+
+ static defaultProps = {
+ width: 840,
+ height: 500,
+ pointArray: [],
+ currentProjection: PROJECTION_TYPES.ALBERS_USA
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ landFeatures: undefined,
+ boundaryMesh: undefined,
+ plotPoints: []
+ };
+
+ this.setCurrentProjection = this.setCurrentProjection.bind(this);
+ this.processAndRenderMapData = this.processAndRenderMapData.bind(this);
+ this.generateLandFeatures = this.generateLandFeatures.bind(this);
+ this.generateBoundaryMesh = this.generateBoundaryMesh.bind(this);
+ this.generatePlotPointArray = this.generatePlotPointArray.bind(this);
+ this.extractNestedObjectInJson = this.extractNestedObjectInJson.bind(this);
+ this.areArraysEqual = this.areArraysEqual.bind(this);
+
+ this.setCurrentProjection(props.currentProjection);
+ this.projection.translate([this.props.width / 2, this.props.height / 2]);
+ this.path = geoPath().projection(this.projection);
+ this.didProjectionTypeChange = true;
+ this.isMapMounted = false;
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (!this.areArraysEqual(this.props.pointArray, nextProps.pointArray)) {
+ if (this.props.currentProjection !== nextProps.currentProjection) {
+ this.didProjectionTypeChange = true;
+ this.setCurrentProjection(nextProps.currentProjection);
+ }
+ if (this.isMapMounted) {
+ this.processAndRenderMapData(nextProps.pointArray);
+ }
+ }
+ }
+
+ componentDidMount() {
+ this.isMapMounted = true;
+ this.processAndRenderMapData(this.props.pointArray);
+ }
+
+ setCurrentProjection(projectionType) {
+ switch (projectionType) {
+ case PROJECTION_TYPES.ALBERS_USA:
+ this.projection = geoAlbersUsa();
+ break;
+
+ case PROJECTION_TYPES.EQUIRECTANGULAR:
+ this.projection = geoEquirectangular();
+ break;
+
+ default:
+ // TODO -> FE logging should be a thing at some point. Maybe a log call
+ // to the BE?
+ break;
+ }
+ }
+
+ processAndRenderMapData(plotPointArray) {
+ let landFeatures = this.state.landFeatures;
+ let boundaryMesh = this.state.boundaryMesh;
+ let plotPoints = this.state.plotPoints;
+
+ switch (this.props.currentProjection) {
+ case PROJECTION_TYPES.ALBERS_USA:
+ if (this.didProjectionTypeChange) {
+ landFeatures =
+ this.generateLandFeatures(usMapJson,
+ MAP_OBJECT_KEYS.ALBERS_USA_LAND_KEYS);
+ boundaryMesh =
+ this.generateBoundaryMesh(usMapJson,
+ MAP_OBJECT_KEYS.ALBERS_USA_BOUNDARY_KEYS);
+ this.didProjectionTypeChange = false;
+ }
+ plotPoints = this.generatePlotPointArray(plotPointArray);
+ break;
+ case PROJECTION_TYPES.EQUIRECTANGULAR:
+ if (this.didProjectionTypeChange) {
+ landFeatures =
+ this.generateLandFeatures(worldMapJson,
+ MAP_OBJECT_KEYS.EQUIRECTANGULAR_LAND_KEYS);
+ boundaryMesh =
+ this.generateBoundaryMesh(worldMapJson,
+ MAP_OBJECT_KEYS.EQUIRECTANGULAR_BOUNDARY_KEYS);
+ this.didProjectionTypeChange = false;
+ }
+ plotPoints = this.generatePlotPointArray(plotPointArray);
+ break;
+ default:
+
+ // TODO -> FE logging should be a thing at some point. Maybe a log call
+ // to the BE?
+ break;
+ }
+
+ this.setState(() => {
+ return {
+ landFeatures: landFeatures,
+ boundaryMesh: boundaryMesh,
+ plotPoints: plotPoints
+ };
+ });
+ }
+
+ generateLandFeatures(jsonData, featureKeys) {
+ let featureType = this.extractNestedObjectInJson(jsonData, featureKeys);
+ let landFeature = undefined;
+ if (featureType !== undefined) {
+ let landFeaturePath = this.path(feature(jsonData, featureType));
+ let landFeatureProps = {
+ className: 'land-features',
+ d: landFeaturePath,
+ key: LAND_FEATURE_KEY
+ };
+ landFeature =
+ React.createElement(PLOT_POINT_SHAPES.PATH, landFeatureProps);
+ }
+ return landFeature;
+ }
+
+ generateBoundaryMesh(jsonData, boundaryKeys) {
+ let boundaryType = this.extractNestedObjectInJson(jsonData, boundaryKeys);
+ let boundary = undefined;
+ if (boundaryType !== undefined) {
+ let boundaryPath = this.path(mesh(jsonData, boundaryType, (a, b) => {
+ return a !== b;
+ }));
+ let boundaryProps = {
+ className: 'boundary-mesh',
+ d: boundaryPath,
+ key: BOUNDARY_MESH_KEY
+ };
+ boundary = React.createElement(PLOT_POINT_SHAPES.PATH, boundaryProps);
+ }
+ return boundary;
+ }
+
+ generatePlotPointArray(pointArray) {
+ let generatedPoints = [];
+ if (pointArray !== undefined && pointArray.length > 0) {
+ generatedPoints = pointArray.map((longLat, index) => {
+ let projectedLongLat = this.projection(
+ [longLat.longitude, longLat.latitude]);
+ let plotPointProps = {
+ className: 'plot-point',
+ r: 4,
+ cx: projectedLongLat[0],
+ cy: projectedLongLat[1],
+ key: PLOT_POINT_KEY_BASE + index,
+ };
+ return React.createElement(PLOT_POINT_SHAPES.CIRCLE, plotPointProps);
+ });
+ }
+ return generatedPoints;
+ }
+
+ render() {
+ let {landFeatures, boundaryMesh, plotPoints} = this.state;
+ let {width, height} = this.props;
+
+ return (
+ <div width={width} height={height}>
+ <svg width={width} height={height}>
+ <g>
+ {landFeatures}
+ {boundaryMesh}
+ {plotPoints}
+ </g>
+ </svg>
+ </div>
+ );
+ }
+
+ extractNestedObjectInJson(jsonData, keysArray) {
+ let subObject = undefined;
+ if (jsonData !== undefined && keysArray !== undefined) {
+ subObject = jsonData[keysArray[0]];
+ if (subObject !== undefined) {
+ for (let i = 1; i < keysArray.length; i++) {
+ subObject = subObject[keysArray[i]];
+ }
+ }
+ }
+ return subObject;
+ }
+
+ areArraysEqual(arrayOne, arrayTwo) {
+ if (arrayOne.length !== arrayTwo.length) {
+ return false;
+ }
+ for (let i = 0; i < arrayOne.length; i++) {
+ if (arrayOne[i] instanceof Array && arrayTwo instanceof Array) {
+ if (!this.areArraysEqual(arrayOne[i], arrayTwo[i])) {
+ return false;
+ }
+ }
+ else if (arrayOne[i] !== arrayTwo[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+export default TopographicMap;