Added native Bazel build to GUI2. Reduced a lot of the unused Angular CLI structures
Reviewers should look at the changes in WORKSPACE, BUILD, BUILD.bazel, README.md files
This is only possible now as rules_nodejs went to 1.0.0 on December 20
gui2 has now been made the entry point (rather than gui2-fw-lib)
No tests or linting are functional yet for Typescript
Each NgModule now has its own BUILD.bazel file with ng_module
gui2-fw-lib is all one module and has been refactored to simplify the directory structure
gui2-topo-lib is also all one module - its directory structure has had 3 layers removed
The big bash script in web/gui2/BUILD has been removed - all is done through ng_module rules
in web/gui2/src/main/webapp/BUILD.bazel and web/gui2/src/main/webapp/app/BUILD.bazel
Change-Id: Ifcfcc23a87be39fe6d6c8324046cc8ebadb90551
diff --git a/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.ts b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.ts
new file mode 100644
index 0000000..993aebf
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/mapsvg/mapsvg.component.ts
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+import {
+ Component, EventEmitter,
+ Input,
+ OnChanges, Output,
+ SimpleChanges
+} from '@angular/core';
+import { MapObject } from '../maputils';
+import {LogService, MapBounds} from '../../../../gui2-fw-lib/public_api';
+import {HttpClient} from '@angular/common/http';
+import * as d3 from 'd3';
+import * as topojson from 'topojson-client';
+
+const BUNDLED_URL_PREFIX = 'data/map/';
+
+/**
+ * Model of the transform attribute of a topojson file
+ */
+interface TopoDataTransform {
+ scale: number[];
+ translate: number[];
+}
+
+/**
+ * Model of the Feature returned prom topojson library
+ */
+interface Feature {
+ geometry: Object;
+ id: string;
+ properties: Object;
+ type: string;
+}
+
+/**
+ * Model of the Features Collection returned by the topojson.features function
+ */
+interface FeatureCollection {
+ type: string;
+ features: Feature[];
+}
+
+/**
+ * Model of the topojson file
+ */
+export interface TopoData {
+ type: string; // Usually "Topology"
+ objects: Object; // Can be a list of countries or individual countries
+ arcs: number[][][]; // Coordinates
+ bbox: number[]; // Bounding box
+ transform: TopoDataTransform; // scale and translate
+}
+
+@Component({
+ selector: '[onos-mapsvg]',
+ templateUrl: './mapsvg.component.html',
+ styleUrls: ['./mapsvg.component.css']
+})
+export class MapSvgComponent implements OnChanges {
+ @Input() map: MapObject = <MapObject>{id: 'none'};
+ @Output() mapBounds = new EventEmitter<MapBounds>();
+
+ geodata: FeatureCollection;
+ pathgen: any; // (Feature) => string; have to leave it general, as there is the bounds method used below
+ // testPath: string;
+ // testFeature = <Feature>{
+ // id: 'test',
+ // type: 'Feature',
+ // geometry: {
+ // coordinates: [
+ // [[-15, 60], [45, 60], [45, 45], [-15, 45], [-15, 60]],
+ // [[-10, 55], [45, 55], [45, 50], [-10, 50], [-10, 55]],
+ // ],
+ // type: 'Polygon'
+ // },
+ // properties: { name: 'Test'}
+ // };
+
+ constructor(
+ private log: LogService,
+ private httpClient: HttpClient,
+ ) {
+ // Scale everything to 360 degrees wide and 150 high
+ // See background.component.html for more details
+ this.pathgen = d3.geoPath().projection(d3.geoIdentity().reflectY(true)
+ // MapSvgComponent.scale()
+ );
+
+ // this.log.debug('Feature Test',this.testFeature);
+ // this.testPath = this.pathgen(this.testFeature);
+ // this.log.debug('Feature Path', this.testPath);
+ this.log.debug('MapSvgComponent constructed');
+ }
+
+ static getUrl(id: string): string {
+ if (id && id[0] === '*') {
+ return BUNDLED_URL_PREFIX + id.slice(1) + '.topojson';
+ }
+ return id + '.topojson';
+ }
+
+ /**
+ * Wrapper for the path generator function
+ * @param feature The county or state within the map
+ */
+ pathGenerator(feature: Feature): string {
+ return this.pathgen(feature);
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ this.log.debug('Change detected', changes);
+ if (changes['map']) {
+ const map: MapObject = <MapObject>(changes['map'].currentValue);
+ if (map.id) {
+ this.httpClient
+ .get(MapSvgComponent.getUrl(map.filePath))
+ .subscribe((topoData: TopoData) => {
+ // this.mapPathGenerator =
+ this.handleTopoJson(map, topoData);
+ this.log.debug('Path Generated for', map.id,
+ 'from', MapSvgComponent.getUrl(map.filePath));
+ });
+ }
+ }
+ }
+
+ /**
+ * Handle the topojson file stream as it arrives back from the server
+ *
+ * The topojson library converts the topojson file in to a FeatureCollection
+ * d3.geo then further converts this in to a Path
+ *
+ * @param map The Map chosen in the GUI
+ * @param topoData The data in the TopoJson file
+ */
+ handleTopoJson(map: MapObject, topoData: TopoData): void {
+
+ let topoObject = topoData.objects[map.id];
+ if (!topoObject) {
+ topoObject = topoData.objects['states'];
+ }
+ this.log.debug('Topo obj', topoObject, 'topodata', topoData);
+ this.geodata = <FeatureCollection>topojson.feature(topoData, topoObject);
+ const bounds = this.pathgen.bounds(this.geodata);
+ this.mapBounds.emit(<MapBounds>{
+ lngMin: bounds[0][0],
+ latMin: -bounds[0][1], // Y was inverted in the transform
+ lngMax: bounds[1][0],
+ latMax: -bounds[1][1] // Y was inverted in the transform
+ });
+ this.log.debug('Map retrieved', topoData, this.geodata);
+
+ }
+}