blob: 23dbeb12753b20ec64ae4002e8b640de0bad94e4 [file] [log] [blame]
Sean Condonf4f54a12018-10-10 23:25:46 +01001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the 'License');
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an 'AS IS' BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Sean Condon0d064ec2019-02-04 21:53:53 +000016import {
17 Component,
18 Input,
19 OnChanges,
20 SimpleChanges
21} from '@angular/core';
22import { MapObject } from '../maputils';
23import {LogService} from 'gui2-fw-lib';
24import {HttpClient} from '@angular/common/http';
25import * as d3 from 'd3';
26import * as topojson from 'topojson-client';
27
28const BUNDLED_URL_PREFIX = 'data/map/';
29
30/**
31 * Model of the transform attribute of a topojson file
32 */
33interface TopoDataTransform {
34 scale: number[];
35 translate: number[];
36}
37
38/**
39 * Model of the Generator setting for D3 GEO
40 */
41interface GeneratorSettings {
42 objectTag: string;
43 projection: Object;
44 logicalSize: number;
45 mapFillScale: number;
46}
47
48/**
Sean Condon0d064ec2019-02-04 21:53:53 +000049 * Model of the Feature returned prom topojson library
50 */
51interface Feature {
52 geometry: Object;
53 id: string;
54 properties: Object;
55 type: string;
56}
57
58/**
59 * Model of the Features Collection returned by the topojson.features function
60 */
61interface FeatureCollection {
62 type: string;
63 features: Feature[];
64}
65
66/**
67 * Model of the topojson file
68 */
69interface TopoData {
70 type: string; // Usually "Topology"
71 objects: Object; // Can be a list of countries or individual countries
72 arcs: number[][][]; // Coordinates
73 bbox: number[]; // Bounding box
74 transform: TopoDataTransform; // scale and translate
75}
76
Sean Condonf4f54a12018-10-10 23:25:46 +010077@Component({
78 selector: '[onos-mapsvg]',
79 templateUrl: './mapsvg.component.html',
80 styleUrls: ['./mapsvg.component.css']
81})
Sean Condon0d064ec2019-02-04 21:53:53 +000082export class MapSvgComponent implements OnChanges {
83 @Input() map: MapObject = <MapObject>{id: 'none'};
Sean Condonf4f54a12018-10-10 23:25:46 +010084
Sean Condon71910542019-02-16 18:16:42 +000085 geodata: FeatureCollection;
86 pathgen: (Feature) => string;
87 // testPath: string;
88 // testFeature = <Feature>{
89 // id: 'test',
90 // type: 'Feature',
91 // geometry: {
92 // coordinates: [
93 // [[-15, 60], [45, 60], [45, 45], [-15, 45], [-15, 60]],
94 // [[-10, 55], [45, 55], [45, 50], [-10, 50], [-10, 55]],
95 // ],
96 // type: 'Polygon'
97 // },
98 // properties: { name: 'Test'}
99 // };
Sean Condonf4f54a12018-10-10 23:25:46 +0100100
Sean Condon0d064ec2019-02-04 21:53:53 +0000101 constructor(
102 private log: LogService,
103 private httpClient: HttpClient,
104 ) {
Sean Condon71910542019-02-16 18:16:42 +0000105 this.pathgen = d3.geoPath().projection(
106 MapSvgComponent.scale(1, 360, 150));
107
108 // this.log.debug('Feature Test',this.testFeature);
109 // this.testPath = this.pathgen(this.testFeature);
110 // this.log.debug('Feature Path', this.testPath);
111
Sean Condon0d064ec2019-02-04 21:53:53 +0000112 this.log.debug('MapSvgComponent constructed');
Sean Condonf4f54a12018-10-10 23:25:46 +0100113 }
114
Sean Condon0d064ec2019-02-04 21:53:53 +0000115 static getUrl(id: string): string {
116 if (id && id[0] === '*') {
117 return BUNDLED_URL_PREFIX + id.slice(1) + '.topojson';
118 }
119 return id + '.topojson';
120 }
121
Sean Condon71910542019-02-16 18:16:42 +0000122 static scale (scaleFactor: number, width: number, height: number) {
123 return d3.geoTransform({
124 point: function(x, y) {
125 this.stream.point( (x - width / 2) * scaleFactor + width / 2,
126 (-y - height / 2) * scaleFactor + height / 2);
Sean Condon0d064ec2019-02-04 21:53:53 +0000127 }
Sean Condon71910542019-02-16 18:16:42 +0000128 });
Sean Condon0d064ec2019-02-04 21:53:53 +0000129 }
130
131 /**
132 * Wrapper for the path generator function
133 * @param feature The county or state within the map
134 */
135 pathGenerator(feature: Feature): string {
Sean Condon71910542019-02-16 18:16:42 +0000136 return this.pathgen(feature);
137 }
138
139 ngOnChanges(changes: SimpleChanges): void {
140 this.log.debug('Change detected', changes);
141 if (changes['map']) {
142 const map: MapObject = <MapObject>(changes['map'].currentValue);
143 if (map.id) {
144 this.httpClient
145 .get(MapSvgComponent.getUrl(map.filePath))
146 .subscribe((topoData: TopoData) => {
147 // this.mapPathGenerator =
148 this.handleTopoJson(map, topoData);
149 this.log.debug('Path Generated for', map.id,
150 'from', MapSvgComponent.getUrl(map.filePath));
151 });
152 }
153 }
Sean Condon0d064ec2019-02-04 21:53:53 +0000154 }
155
156 /**
157 * Handle the topojson file stream as it arrives back from the server
158 *
159 * The topojson library converts the topojson file in to a FeatureCollection
160 * d3.geo then further converts this in to a Path
161 *
162 * @param map The Map chosen in the GUI
163 * @param topoData The data in the TopoJson file
164 */
Sean Condon71910542019-02-16 18:16:42 +0000165 handleTopoJson(map: MapObject, topoData: TopoData): void {
Sean Condon0d064ec2019-02-04 21:53:53 +0000166
Sean Condon71910542019-02-16 18:16:42 +0000167 let topoObject = topoData.objects[map.id];
168 if (!topoObject) {
169 topoObject = topoData.objects['states'];
170 }
171 this.log.debug('Topo obj', topoObject, 'topodata', topoData);
172 this.geodata = <FeatureCollection>topojson.feature(topoData, topoObject);
173 this.log.debug('Map retrieved', topoData, this.geodata);
Sean Condon0d064ec2019-02-04 21:53:53 +0000174
Sean Condon0d064ec2019-02-04 21:53:53 +0000175 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100176}