mempool/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts

225 lines
6.7 KiB
TypeScript
Raw Normal View History

import { ChangeDetectionStrategy, Component, HostListener, Input, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { SeoService } from 'src/app/services/seo.service';
import { ApiService } from 'src/app/services/api.service';
2022-07-23 15:43:38 +02:00
import { Observable, switchMap, tap, zip } from 'rxjs';
import { AssetsService } from 'src/app/services/assets.service';
2022-07-23 15:43:38 +02:00
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
import { StateService } from 'src/app/services/state.service';
import { EChartsOption, registerMap } from 'echarts';
import 'echarts-gl';
@Component({
selector: 'app-nodes-channels-map',
templateUrl: './nodes-channels-map.component.html',
styleUrls: ['./nodes-channels-map.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NodesChannelsMap implements OnInit, OnDestroy {
2022-07-23 15:43:38 +02:00
@Input() style: 'graph' | 'nodepage' | 'widget' = 'graph';
@Input() publicKey: string | undefined;
observable$: Observable<any>;
center: number[] | undefined = undefined;
chartInstance = undefined;
2022-07-23 14:23:47 +02:00
chartOptions: EChartsOption = {};
chartInitOptions = {
renderer: 'canvas',
};
constructor(
private seoService: SeoService,
private apiService: ApiService,
private stateService: StateService,
private assetsService: AssetsService,
private router: Router,
private zone: NgZone,
2022-07-23 15:43:38 +02:00
private activatedRoute: ActivatedRoute,
) {
}
ngOnDestroy(): void {}
ngOnInit(): void {
this.center = this.style === 'widget' ? [0, 0, -10] : undefined;
2022-07-23 15:43:38 +02:00
if (this.style === 'graph') {
this.seoService.setTitle($localize`Lightning nodes channels world map`);
}
this.observable$ = this.activatedRoute.paramMap
.pipe(
switchMap((params: ParamMap) => {
return zip(
this.assetsService.getWorldMapJson$,
2022-07-23 19:03:12 +02:00
this.apiService.getChannelsGeo$(params.get('public_key') ?? undefined),
[params.get('public_key') ?? undefined]
2022-07-23 15:43:38 +02:00
).pipe(tap((data) => {
registerMap('world', data[0]);
2022-07-23 15:43:38 +02:00
const channelsLoc = [];
const nodes = [];
const nodesPubkeys = {};
let thisNodeGPS: number[] | undefined = undefined;
2022-07-23 15:43:38 +02:00
for (const channel of data[1]) {
if (!thisNodeGPS && data[2] === channel[0]) {
thisNodeGPS = [channel[2], channel[3]];
} else if (!thisNodeGPS && data[2] === channel[4]) {
thisNodeGPS = [channel[6], channel[7]];
}
2022-07-23 15:43:38 +02:00
channelsLoc.push([[channel[2], channel[3]], [channel[6], channel[7]]]);
if (!nodesPubkeys[channel[0]]) {
nodes.push({
publicKey: channel[0],
name: channel[1],
value: [channel[2], channel[3]],
});
nodesPubkeys[channel[0]] = true;
}
if (!nodesPubkeys[channel[4]]) {
nodes.push({
publicKey: channel[4],
name: channel[5],
value: [channel[6], channel[7]],
});
nodesPubkeys[channel[4]] = true;
}
2022-07-23 15:43:38 +02:00
}
if (this.style === 'nodepage' && thisNodeGPS) {
// 1ML 0217890e3aad8d35bc054f43acc00084b25229ecff0ab68debd82883ad65ee8266
// New York GPS [-74.0068, 40.7123]
// Map center [-20.55, 0, -9.85]
this.center = [thisNodeGPS[0] * -20.55 / -74.0068, 0, thisNodeGPS[1] * -9.85 / 40.7123];
}
2022-07-23 15:43:38 +02:00
this.prepareChartOptions(nodes, channelsLoc);
}));
})
);
}
prepareChartOptions(nodes, channels) {
let title: object;
if (channels.length === 0) {
title = {
textStyle: {
color: 'grey',
fontSize: 15
},
2022-07-23 15:43:38 +02:00
text: $localize`No geolocation data available`,
left: 'center',
top: 'center'
};
}
this.chartOptions = {
silent: this.style === 'widget' ? true : false,
2022-07-23 15:43:38 +02:00
title: title ?? undefined,
geo3D: {
map: 'world',
shading: 'color',
silent: true,
postEffect: {
enable: true,
bloom: {
intensity: 0.1,
}
},
viewControl: {
center: this.center,
2022-08-01 18:55:35 +02:00
minDistance: 1,
maxDistance: 60,
distance: this.style === 'widget' ? 22 : this.style === 'nodepage' ? 22 : 60,
2022-07-23 14:23:47 +02:00
alpha: 90,
2022-08-01 18:55:35 +02:00
rotateSensitivity: 0,
panSensitivity: this.style === 'widget' ? 0 : 1,
zoomSensitivity: this.style === 'widget' ? 0 : 0.5,
panMouseButton: this.style === 'widget' ? null : 'left',
2022-07-23 19:03:12 +02:00
rotateMouseButton: undefined,
},
itemStyle: {
color: 'white',
opacity: 0.02,
borderWidth: 1,
borderColor: 'black',
},
regionHeight: 0.01,
},
series: [
{
// @ts-ignore
type: 'lines3D',
coordinateSystem: 'geo3D',
blendMode: 'lighter',
lineStyle: {
width: 1,
2022-07-23 19:03:12 +02:00
opacity: ['widget', 'graph'].includes(this.style) ? 0.025 : 1,
},
data: channels
},
{
// @ts-ignore
type: 'scatter3D',
symbol: 'circle',
blendMode: 'lighter',
coordinateSystem: 'geo3D',
symbolSize: 3,
itemStyle: {
color: '#BBFFFF',
opacity: 1,
borderColor: '#FFFFFF00',
},
data: nodes,
emphasis: {
label: {
position: 'top',
2022-07-23 14:23:47 +02:00
color: 'white',
fontSize: 16,
formatter: function(value) {
return value.name;
},
show: true,
}
}
},
]
};
}
@HostListener('window:wheel', ['$event'])
onWindowScroll(e): void {
// Not very smooth when using the mouse
if (this.style === 'widget' && e.target.tagName === 'CANVAS') {
window.scrollBy({left: 0, top: e.deltaY, behavior: 'auto'});
}
}
onChartInit(ec) {
if (this.chartInstance !== undefined) {
return;
}
this.chartInstance = ec;
if (this.style === 'widget') {
this.chartInstance.getZr().on('click', (e) => {
this.zone.run(() => {
const url = new RelativeUrlPipe(this.stateService).transform(`/graphs/lightning/nodes-channels-map`);
this.router.navigate([url]);
});
});
}
this.chartInstance.on('click', (e) => {
if (e.data && e.data.publicKey) {
this.zone.run(() => {
const url = new RelativeUrlPipe(this.stateService).transform(`/lightning/node/${e.data.publicKey}`);
this.router.navigate([url]);
});
}
});
}
}