mirror of
https://github.com/mempool/mempool.git
synced 2025-02-22 22:25:34 +01:00
Create world map of clearnet LN nodes and channels
This commit is contained in:
parent
c7909a1ca8
commit
c839abb479
12 changed files with 329 additions and 2 deletions
|
@ -13,6 +13,30 @@ class ChannelsApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async $getAllChannelsGeo(): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
const query = `SELECT nodes_1.public_key as node1_public_key, nodes_1.alias AS node1_alias,
|
||||||
|
nodes_1.latitude AS node1_latitude, nodes_1.longitude AS node1_longitude,
|
||||||
|
nodes_2.public_key as node2_public_key, nodes_2.alias AS node2_alias,
|
||||||
|
nodes_2.latitude AS node2_latitude, nodes_2.longitude AS node2_longitude,
|
||||||
|
channels.capacity
|
||||||
|
FROM channels
|
||||||
|
JOIN nodes AS nodes_1 on nodes_1.public_key = channels.node1_public_key
|
||||||
|
JOIN nodes AS nodes_2 on nodes_2.public_key = channels.node2_public_key
|
||||||
|
WHERE nodes_1.latitude IS NOT NULL AND nodes_1.longitude IS NOT NULL
|
||||||
|
AND nodes_2.latitude IS NOT NULL AND nodes_2.longitude IS NOT NULL
|
||||||
|
`;
|
||||||
|
const [rows]: any = await DB.query(query);
|
||||||
|
return rows.map((row) => [
|
||||||
|
row.node1_public_key, row.node1_alias, row.node1_longitude, row.node1_latitude,
|
||||||
|
row.node2_public_key, row.node2_alias, row.node2_longitude, row.node2_latitude,
|
||||||
|
row.capacity]);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$getAllChannels error: ' + (e instanceof Error ? e.message : e));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async $searchChannelsById(search: string): Promise<any[]> {
|
public async $searchChannelsById(search: string): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const searchStripped = search.replace('%', '') + '%';
|
const searchStripped = search.replace('%', '') + '%';
|
||||||
|
|
|
@ -11,6 +11,7 @@ class ChannelsRoutes {
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/search/:search', this.$searchChannelsById)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/search/:search', this.$searchChannelsById)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/:short_id', this.$getChannel)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels/:short_id', this.$getChannel)
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels', this.$getChannelsForNode)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels', this.$getChannelsForNode)
|
||||||
|
.get(config.MEMPOOL.API_URL_PREFIX + 'lightning/channels-geo', this.$getChannelsGeo)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +94,15 @@ class ChannelsRoutes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async $getChannelsGeo(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const channels = await channelsApi.$getAllChannelsGeo();
|
||||||
|
res.json(channels);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ChannelsRoutes();
|
export default new ChannelsRoutes();
|
||||||
|
|
32
frontend/package-lock.json
generated
32
frontend/package-lock.json
generated
|
@ -34,6 +34,7 @@
|
||||||
"clipboard": "^2.0.10",
|
"clipboard": "^2.0.10",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"echarts": "~5.3.2",
|
"echarts": "~5.3.2",
|
||||||
|
"echarts-gl": "^2.0.9",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"lightweight-charts": "~3.8.0",
|
"lightweight-charts": "~3.8.0",
|
||||||
"ngx-echarts": "8.0.1",
|
"ngx-echarts": "8.0.1",
|
||||||
|
@ -6396,6 +6397,11 @@
|
||||||
"webpack": ">=4.0.1"
|
"webpack": ">=4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/claygl": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
|
||||||
|
},
|
||||||
"node_modules/clean-stack": {
|
"node_modules/clean-stack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
|
@ -8107,6 +8113,18 @@
|
||||||
"zrender": "5.3.1"
|
"zrender": "5.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/echarts-gl": {
|
||||||
|
"version": "2.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
|
||||||
|
"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
|
||||||
|
"dependencies": {
|
||||||
|
"claygl": "^1.2.1",
|
||||||
|
"zrender": "^5.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"echarts": "^5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/echarts/node_modules/tslib": {
|
"node_modules/echarts/node_modules/tslib": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||||
|
@ -22520,6 +22538,11 @@
|
||||||
"integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
|
"integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"claygl": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
|
||||||
|
},
|
||||||
"clean-stack": {
|
"clean-stack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
|
@ -23866,6 +23889,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"echarts-gl": {
|
||||||
|
"version": "2.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz",
|
||||||
|
"integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==",
|
||||||
|
"requires": {
|
||||||
|
"claygl": "^1.2.1",
|
||||||
|
"zrender": "^5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ee-first": {
|
"ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
"clipboard": "^2.0.10",
|
"clipboard": "^2.0.10",
|
||||||
"domino": "^2.1.6",
|
"domino": "^2.1.6",
|
||||||
"echarts": "~5.3.2",
|
"echarts": "~5.3.2",
|
||||||
|
"echarts-gl": "^2.0.9",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"lightweight-charts": "~3.8.0",
|
"lightweight-charts": "~3.8.0",
|
||||||
"ngx-echarts": "8.0.1",
|
"ngx-echarts": "8.0.1",
|
||||||
|
|
|
@ -39,7 +39,9 @@
|
||||||
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-per-country' | relativeUrl]"
|
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-per-country' | relativeUrl]"
|
||||||
i18n="lightning.nodes-per-isp">Lightning nodes per country</a>
|
i18n="lightning.nodes-per-isp">Lightning nodes per country</a>
|
||||||
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-map' | relativeUrl]"
|
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-map' | relativeUrl]"
|
||||||
i18n="lightning.nodes-per-isp">Lightning nodes world map</a>
|
i18n="lightning.lightning.nodes-heatmap">Lightning nodes world map</a>
|
||||||
|
<a class="dropdown-item" routerLinkActive="active" [routerLink]="['/graphs/lightning/nodes-channels-map' | relativeUrl]"
|
||||||
|
i18n="lightning.nodes-channels-world-map">Lightning nodes channels world map</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { LightningStatisticsChartComponent } from '../lightning/statistics-chart
|
||||||
import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component';
|
import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component';
|
||||||
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
||||||
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
||||||
|
import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
|
||||||
|
|
||||||
const browserWindow = window || {};
|
const browserWindow = window || {};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -114,6 +115,10 @@ const routes: Routes = [
|
||||||
path: 'lightning/nodes-map',
|
path: 'lightning/nodes-map',
|
||||||
component: NodesMap,
|
component: NodesMap,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'lightning/nodes-channels-map',
|
||||||
|
component: NodesChannelsMap,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
redirectTo: 'mempool',
|
redirectTo: 'mempool',
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { NodesPerCountry } from './nodes-per-country/nodes-per-country.component
|
||||||
import { NodesPerISP } from './nodes-per-isp/nodes-per-isp.component';
|
import { NodesPerISP } from './nodes-per-isp/nodes-per-isp.component';
|
||||||
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component';
|
||||||
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
||||||
|
import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
LightningDashboardComponent,
|
LightningDashboardComponent,
|
||||||
|
@ -43,6 +44,7 @@ import { NodesMap } from '../lightning/nodes-map/nodes-map.component';
|
||||||
NodesPerISP,
|
NodesPerISP,
|
||||||
NodesPerCountryChartComponent,
|
NodesPerCountryChartComponent,
|
||||||
NodesMap,
|
NodesMap,
|
||||||
|
NodesChannelsMap,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="full-container">
|
||||||
|
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="d-flex d-md-block align-items-baseline" style="margin-bottom: -5px">
|
||||||
|
<span i18n="lightning.nodes-channels-world-map">Lightning nodes channels world map</span>
|
||||||
|
<button class="btn p-0 pl-2" style="margin: 0 0 4px 0px">
|
||||||
|
<fa-icon [icon]="['fas', 'download']" [fixedWidth]="true" (click)="onSaveChart()"></fa-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<small style="color: #ffffff66" i18n="lightning.tor-nodes-excluded">(Tor nodes excluded)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="observable$ | async" class="chart" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||||
|
(chartInit)="onChartInit($event)">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,40 @@
|
||||||
|
.card-header {
|
||||||
|
border-bottom: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
@media (min-width: 465px) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-container {
|
||||||
|
padding: 0px 15px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 500px;
|
||||||
|
height: calc(100% - 150px);
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
padding-right: 10px;
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
padding-bottom: 25px;
|
||||||
|
}
|
||||||
|
@media (max-width: 829px) {
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
padding-bottom: 25px;
|
||||||
|
}
|
||||||
|
@media (max-width: 629px) {
|
||||||
|
padding-bottom: 55px;
|
||||||
|
}
|
||||||
|
@media (max-width: 567px) {
|
||||||
|
padding-bottom: 55px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
import { ChangeDetectionStrategy, Component, NgZone, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
|
import { Observable, tap, zip } from 'rxjs';
|
||||||
|
import { AssetsService } from 'src/app/services/assets.service';
|
||||||
|
import { download } from 'src/app/shared/graphs.utils';
|
||||||
|
import { 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';
|
||||||
|
import { SSL_OP_SSLEAY_080_CLIENT_DH_BUG } from 'constants';
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
observable$: Observable<any>;
|
||||||
|
|
||||||
|
chartInstance = undefined;
|
||||||
|
chartOptions: EChartsOption = {color: 'dark'};
|
||||||
|
chartInitOptions = {
|
||||||
|
renderer: 'canvas',
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private seoService: SeoService,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private stateService: StateService,
|
||||||
|
private assetsService: AssetsService,
|
||||||
|
private router: Router,
|
||||||
|
private zone: NgZone,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.seoService.setTitle($localize`Lightning nodes channels world map`);
|
||||||
|
|
||||||
|
this.observable$ = zip(
|
||||||
|
this.assetsService.getWorldMapJson$,
|
||||||
|
this.apiService.getChannelsGeo$(),
|
||||||
|
).pipe(tap((data) => {
|
||||||
|
registerMap('world', data[0]);
|
||||||
|
|
||||||
|
const channelsLoc = [];
|
||||||
|
const nodes = [];
|
||||||
|
for (const channel of data[1]) {
|
||||||
|
channelsLoc.push([[channel[2], channel[3]], [channel[6], channel[7]]]);
|
||||||
|
nodes.push({
|
||||||
|
publicKey: channel[0],
|
||||||
|
name: channel[1],
|
||||||
|
value: [channel[2], channel[3]],
|
||||||
|
});
|
||||||
|
nodes.push({
|
||||||
|
publicKey: channel[4],
|
||||||
|
name: channel[5],
|
||||||
|
value: [channel[6], channel[7]],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prepareChartOptions(nodes, channelsLoc);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareChartOptions(nodes, channels) {
|
||||||
|
let title: object;
|
||||||
|
if (channels.length === 0) {
|
||||||
|
title = {
|
||||||
|
textStyle: {
|
||||||
|
color: 'grey',
|
||||||
|
fontSize: 15
|
||||||
|
},
|
||||||
|
text: $localize`No data to display yet`,
|
||||||
|
left: 'center',
|
||||||
|
top: 'center'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chartOptions = {
|
||||||
|
geo3D: {
|
||||||
|
map: 'world',
|
||||||
|
shading: 'color',
|
||||||
|
silent: true,
|
||||||
|
environment: '#000',
|
||||||
|
postEffect: {
|
||||||
|
enable: true,
|
||||||
|
bloom: {
|
||||||
|
intensity: 0.01,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
viewControl: {
|
||||||
|
minDistance: 1,
|
||||||
|
distance: 60,
|
||||||
|
alpha: 89,
|
||||||
|
panMouseButton: 'left',
|
||||||
|
rotateMouseButton: 'right',
|
||||||
|
zoomSensivity: 0.5,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
opacity: 0.02,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'black',
|
||||||
|
},
|
||||||
|
regionHeight: 0.01,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
// @ts-ignore
|
||||||
|
type: 'lines3D',
|
||||||
|
coordinateSystem: 'geo3D',
|
||||||
|
blendMode: 'lighter',
|
||||||
|
lineStyle: {
|
||||||
|
width: 1,
|
||||||
|
opacity: 0.025,
|
||||||
|
},
|
||||||
|
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',
|
||||||
|
// @ts-ignore
|
||||||
|
textStyle: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { combineLatest, Observable, tap } from 'rxjs';
|
import { combineLatest, Observable, tap } from 'rxjs';
|
||||||
import { AssetsService } from 'src/app/services/assets.service';
|
import { AssetsService } from 'src/app/services/assets.service';
|
||||||
import { EChartsOption, MapSeriesOption, registerMap } from 'echarts';
|
import { EChartsOption, registerMap } from 'echarts';
|
||||||
import { download } from 'src/app/shared/graphs.utils';
|
import { download } from 'src/app/shared/graphs.utils';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||||
|
|
|
@ -266,4 +266,8 @@ export class ApiService {
|
||||||
getNodesPerCountry(): Observable<any> {
|
getNodesPerCountry(): Observable<any> {
|
||||||
return this.httpClient.get<any[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/countries');
|
return this.httpClient.get<any[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/nodes/countries');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getChannelsGeo$(): Observable<any> {
|
||||||
|
return this.httpClient.get<any[]>(this.apiBaseUrl + this.apiBasePath + '/api/v1/lightning/channels-geo');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue