2022-07-23 14:23:47 +02:00
|
|
|
import { ChangeDetectionStrategy, Component, Input, NgZone, OnDestroy, OnInit } from '@angular/core';
|
2022-07-21 22:43:12 +02:00
|
|
|
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';
|
2022-07-21 22:43:12 +02:00
|
|
|
import { AssetsService } from 'src/app/services/assets.service';
|
|
|
|
import { download } from 'src/app/shared/graphs.utils';
|
2022-07-23 15:43:38 +02:00
|
|
|
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
|
2022-07-21 22:43:12 +02:00
|
|
|
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;
|
|
|
|
|
2022-07-21 22:43:12 +02:00
|
|
|
observable$: Observable<any>;
|
|
|
|
|
|
|
|
chartInstance = undefined;
|
2022-07-23 14:23:47 +02:00
|
|
|
chartOptions: EChartsOption = {};
|
2022-07-21 22:43:12 +02:00
|
|
|
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,
|
2022-07-21 22:43:12 +02:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
|
|
|
ngOnDestroy(): void {}
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
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),
|
2022-07-23 15:43:38 +02:00
|
|
|
).pipe(tap((data) => {
|
|
|
|
registerMap('world', data[0]);
|
2022-07-21 22:43:12 +02:00
|
|
|
|
2022-07-23 15:43:38 +02:00
|
|
|
const channelsLoc = [];
|
|
|
|
const nodes = [];
|
2022-07-24 15:08:48 +02:00
|
|
|
const nodesPubkeys = {};
|
2022-07-23 15:43:38 +02:00
|
|
|
for (const channel of data[1]) {
|
|
|
|
channelsLoc.push([[channel[2], channel[3]], [channel[6], channel[7]]]);
|
2022-07-24 15:08:48 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
this.prepareChartOptions(nodes, channelsLoc);
|
|
|
|
}));
|
|
|
|
})
|
|
|
|
);
|
2022-07-21 22:43:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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`,
|
2022-07-21 22:43:12 +02:00
|
|
|
left: 'center',
|
|
|
|
top: 'center'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
this.chartOptions = {
|
2022-07-23 15:43:38 +02:00
|
|
|
title: title ?? undefined,
|
2022-07-21 22:43:12 +02:00
|
|
|
geo3D: {
|
|
|
|
map: 'world',
|
|
|
|
shading: 'color',
|
|
|
|
silent: true,
|
|
|
|
postEffect: {
|
|
|
|
enable: true,
|
|
|
|
bloom: {
|
2022-07-24 15:08:48 +02:00
|
|
|
intensity: 0.1,
|
2022-07-21 22:43:12 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
viewControl: {
|
2022-07-23 19:03:12 +02:00
|
|
|
center: this.style === 'widget' ? [0, 0, -1] : undefined,
|
2022-07-23 14:23:47 +02:00
|
|
|
minDistance: 0.1,
|
2022-07-23 19:03:12 +02:00
|
|
|
distance: this.style === 'widget' ? 45 : 60,
|
2022-07-23 14:23:47 +02:00
|
|
|
alpha: 90,
|
2022-07-21 22:43:12 +02:00
|
|
|
panMouseButton: 'left',
|
2022-07-23 19:03:12 +02:00
|
|
|
rotateMouseButton: undefined,
|
2022-07-21 22:43:12 +02:00
|
|
|
zoomSensivity: 0.5,
|
|
|
|
},
|
|
|
|
itemStyle: {
|
2022-07-24 15:08:48 +02:00
|
|
|
color: 'white',
|
2022-07-21 22:43:12 +02:00
|
|
|
opacity: 0.02,
|
|
|
|
borderWidth: 1,
|
2022-07-24 15:08:48 +02:00
|
|
|
borderColor: 'black',
|
2022-07-21 22:43:12 +02:00
|
|
|
},
|
|
|
|
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,
|
2022-07-21 22:43:12 +02:00
|
|
|
},
|
|
|
|
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,
|
2022-07-21 22:43:12 +02:00
|
|
|
formatter: function(value) {
|
|
|
|
return value.name;
|
|
|
|
},
|
|
|
|
show: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
onChartInit(ec) {
|
|
|
|
if (this.chartInstance !== undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.chartInstance = ec;
|
|
|
|
|
|
|
|
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]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
onSaveChart() {
|
|
|
|
// @ts-ignore
|
|
|
|
const prevBottom = this.chartOptions.grid.bottom;
|
|
|
|
const now = new Date();
|
|
|
|
// @ts-ignore
|
|
|
|
this.chartOptions.grid.bottom = 30;
|
|
|
|
this.chartOptions.backgroundColor = '#11131f';
|
|
|
|
this.chartInstance.setOption(this.chartOptions);
|
|
|
|
download(this.chartInstance.getDataURL({
|
|
|
|
pixelRatio: 2,
|
|
|
|
excludeComponents: ['dataZoom'],
|
|
|
|
}), `lightning-nodes-heatmap-clearnet-${Math.round(now.getTime() / 1000)}.svg`);
|
|
|
|
// @ts-ignore
|
|
|
|
this.chartOptions.grid.bottom = prevBottom;
|
|
|
|
this.chartOptions.backgroundColor = 'none';
|
|
|
|
this.chartInstance.setOption(this.chartOptions);
|
|
|
|
}
|
|
|
|
}
|