From b271793d48375c41bd639a5717a4128027dc4e87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr>
Date: Thu, 22 Jul 2021 14:37:55 +0200
Subject: [PATCH] Detail module => done

---
 client/package.json                           |   2 +
 .../default-object.component.html             |   4 +-
 .../{ => default}/default-object.component.ts |   4 +-
 .../detail/components/default/index.ts        |   5 +
 .../app/instance/detail/components/index.ts   |   9 +
 .../components/object-data.component.ts       |   8 +-
 .../detail/components/spectra/index.ts        |   7 +
 .../spectra/spectra-object.component.html     |   7 +-
 .../spectra/spectra-object.component.ts       |   6 +-
 .../detail/containers/detail.component.html   |  21 +-
 .../detail/containers/detail.component.ts     |  81 ++-
 .../instance/detail/detail-routing.module.ts  |   7 +-
 .../src/app/instance/detail/detail.module.ts  |   6 +-
 .../app/instance/instance-routing.module.ts   |   3 +-
 client/src/app/instance/instance.reducer.ts   |   5 +-
 .../result/datatable-tab.component.html       |   1 +
 .../result/datatable-tab.component.ts         |   1 +
 .../search/containers/result.component.html   |   1 +
 .../datatable/datatable.component.html        |   1 +
 .../datatable/datatable.component.ts          |   1 +
 .../renderer/detail-renderer.component.html   |   2 +-
 .../renderer/detail-renderer.component.ts     |   1 +
 .../instance/store/actions/detail.actions.ts  |  17 +
 .../instance/store/effects/detail.effects.ts  |  80 +++
 .../src/app/instance/store/effects/index.ts   |   4 +-
 .../instance/store/reducers/detail.reducer.ts |  71 +++
 .../store/selectors/detail.selector.ts        |  53 ++
 .../store/selectors/search.selector.ts        |  34 +-
 .../instance/store/services/detail.service.ts |  53 ++
 .../src/app/instance/store/services/index.ts  |   4 +-
 client/yarn.lock                              | 478 +++++++++++++++++-
 31 files changed, 897 insertions(+), 80 deletions(-)
 rename client/src/app/instance/detail/components/{ => default}/default-object.component.html (73%)
 rename client/src/app/instance/detail/components/{ => default}/default-object.component.ts (89%)
 create mode 100644 client/src/app/instance/detail/components/default/index.ts
 create mode 100644 client/src/app/instance/detail/components/index.ts
 create mode 100644 client/src/app/instance/detail/components/spectra/index.ts
 create mode 100644 client/src/app/instance/store/actions/detail.actions.ts
 create mode 100644 client/src/app/instance/store/effects/detail.effects.ts
 create mode 100644 client/src/app/instance/store/reducers/detail.reducer.ts
 create mode 100644 client/src/app/instance/store/selectors/detail.selector.ts
 create mode 100644 client/src/app/instance/store/services/detail.service.ts

diff --git a/client/package.json b/client/package.json
index 7e7cad96..587d474e 100644
--- a/client/package.json
+++ b/client/package.json
@@ -26,6 +26,7 @@
     "@ngrx/store": "12.1.0",
     "@ngrx/store-devtools": "12.1.0",
     "bootstrap": "4.6",
+    "d3": "^5.15.1",
     "keycloak-angular": "^8.2.0",
     "keycloak-js": "^14.0.0",
     "ngx-bootstrap": "^7.0.0-rc.1",
@@ -39,6 +40,7 @@
     "@angular-devkit/build-angular": "~12.0.4",
     "@angular/cli": "~12.0.4",
     "@angular/compiler-cli": "~12.0.4",
+    "@types/d3": "^5.7.2",
     "@types/jasmine": "~3.6.0",
     "@types/node": "^12.11.1",
     "jasmine-core": "~3.7.0",
diff --git a/client/src/app/instance/detail/components/default-object.component.html b/client/src/app/instance/detail/components/default/default-object.component.html
similarity index 73%
rename from client/src/app/instance/detail/components/default-object.component.html
rename to client/src/app/instance/detail/components/default/default-object.component.html
index de8546af..bc6843ae 100644
--- a/client/src/app/instance/detail/components/default-object.component.html
+++ b/client/src/app/instance/detail/components/default/default-object.component.html
@@ -1,9 +1,9 @@
 <div class="row justify-content-center">
     <div class="col col-lg-10 col-xl-8 mt-4">
         <app-object-data
-            [datasetName]="datasetName"
+            [datasetSelected]="datasetSelected"
             [outputFamilyList]="outputFamilyList"
-            [categoryList]="categoryList"
+            [outputCategoryList]="outputCategoryList"
             [attributeList]="attributeList"
             [object]="object">
         </app-object-data>
diff --git a/client/src/app/instance/detail/components/default-object.component.ts b/client/src/app/instance/detail/components/default/default-object.component.ts
similarity index 89%
rename from client/src/app/instance/detail/components/default-object.component.ts
rename to client/src/app/instance/detail/components/default/default-object.component.ts
index aed6a148..058a6eda 100644
--- a/client/src/app/instance/detail/components/default-object.component.ts
+++ b/client/src/app/instance/detail/components/default/default-object.component.ts
@@ -21,9 +21,9 @@ import { Attribute, OutputFamily, OutputCategory } from 'src/app/metamodel/model
  * @classdesc Detail default object component.
  */
 export class DefaultObjectComponent {
-    @Input() datasetName: string;
+    @Input() datasetSelected: string;
     @Input() outputFamilyList: OutputFamily[];
-    @Input() categoryList: OutputCategory[];
+    @Input() outputCategoryList: OutputCategory[];
     @Input() attributeList: Attribute[];
     @Input() object: any;
 }
diff --git a/client/src/app/instance/detail/components/default/index.ts b/client/src/app/instance/detail/components/default/index.ts
new file mode 100644
index 00000000..f36eed11
--- /dev/null
+++ b/client/src/app/instance/detail/components/default/index.ts
@@ -0,0 +1,5 @@
+import { DefaultObjectComponent } from './default-object.component';
+
+export const defaultComponents = [
+    DefaultObjectComponent
+];
diff --git a/client/src/app/instance/detail/components/index.ts b/client/src/app/instance/detail/components/index.ts
new file mode 100644
index 00000000..e0d32c36
--- /dev/null
+++ b/client/src/app/instance/detail/components/index.ts
@@ -0,0 +1,9 @@
+import { defaultComponents } from './default';
+import { spectraComponents } from './spectra';
+import { ObjectDataComponent } from './object-data.component';
+
+export const dummiesComponents = [
+    defaultComponents,
+    spectraComponents,
+    ObjectDataComponent
+];
diff --git a/client/src/app/instance/detail/components/object-data.component.ts b/client/src/app/instance/detail/components/object-data.component.ts
index 6d33ca2f..d834874b 100644
--- a/client/src/app/instance/detail/components/object-data.component.ts
+++ b/client/src/app/instance/detail/components/object-data.component.ts
@@ -22,9 +22,9 @@ import { getHost } from 'src/app/shared/utils';
  * @classdesc Detail object data component.
  */
 export class ObjectDataComponent {
-    @Input() datasetName: string;
+    @Input() datasetSelected: string;
     @Input() outputFamilyList: OutputFamily[];
-    @Input() categoryList: OutputCategory[];
+    @Input() outputCategoryList: OutputCategory[];
     @Input() attributeList: Attribute[];
     @Input() object: any;
 
@@ -36,7 +36,7 @@ export class ObjectDataComponent {
      * @return Category[]
      */
     getCategoryByFamilySortedByDisplay(idFamily: number): OutputCategory[] {
-        return this.categoryList
+        return this.outputCategoryList
             .filter(category => category.id_output_family === idFamily)
             //.sort(sortByDisplay);
     }
@@ -78,6 +78,6 @@ export class ObjectDataComponent {
     }
 
     getDownloadHref(value: string) {
-        return getHost() + '/download-file/' + this.datasetName + '/' + value;
+        return getHost() + '/download-file/' + this.datasetSelected + '/' + value;
     }
 }
diff --git a/client/src/app/instance/detail/components/spectra/index.ts b/client/src/app/instance/detail/components/spectra/index.ts
new file mode 100644
index 00000000..77b5e039
--- /dev/null
+++ b/client/src/app/instance/detail/components/spectra/index.ts
@@ -0,0 +1,7 @@
+import { SpectraObjectComponent } from "./spectra-object.component";
+import { SpectraGraphComponent } from "./graph/spectra-graph.component";
+
+export const spectraComponents = [
+    SpectraObjectComponent,
+    SpectraGraphComponent
+];
diff --git a/client/src/app/instance/detail/components/spectra/spectra-object.component.html b/client/src/app/instance/detail/components/spectra/spectra-object.component.html
index 734a18f1..a5c472cc 100644
--- a/client/src/app/instance/detail/components/spectra/spectra-object.component.html
+++ b/client/src/app/instance/detail/components/spectra/spectra-object.component.html
@@ -17,7 +17,8 @@
                 <div class="row justify-content-center">
                     <div class="col-auto">
                         <a [href]="getSpectra()" target="_blank" class="btn btn-lg btn-block dl-btn">
-                            <span [inlineSVG]="'assets/logo_spectra.svg'" title="Download SPECTRA archive"></span>
+                            <!-- <span [inlineSVG]="'assets/logo_spectra.svg'" title="Download SPECTRA archive"></span> -->
+                            Download SPECTRA archive
                         </a>
                     </div>
                 </div>
@@ -25,9 +26,9 @@
         </div>
 
         <app-object-data
-            [datasetName]="datasetName"
+            [datasetSelected]="datasetSelected"
             [outputFamilyList]="outputFamilyList"
-            [categoryList]="categoryList"
+            [outputCategoryList]="outputCategoryList"
             [attributeList]="attributeList"
             [object]="object">
         </app-object-data>
diff --git a/client/src/app/instance/detail/components/spectra/spectra-object.component.ts b/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
index b0cfc9ba..df397e7e 100644
--- a/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
+++ b/client/src/app/instance/detail/components/spectra/spectra-object.component.ts
@@ -25,9 +25,9 @@ import { getHost } from 'src/app/shared/utils';
  * @implements OnInit
  */
 export class SpectraObjectComponent implements OnInit {
-    @Input() datasetName: string;
+    @Input() datasetSelected: string;
     @Input() outputFamilyList: OutputFamily[];
-    @Input() categoryList: OutputCategory[];
+    @Input() outputCategoryList: OutputCategory[];
     @Input() attributeList: Attribute[];
     @Input() object: any;
     @Input() spectraIsLoading: boolean;
@@ -51,7 +51,7 @@ export class SpectraObjectComponent implements OnInit {
         const spectraAttribute: Attribute = this.attributeList
             .filter(a => a.detail)
             .find(attribute => attribute.search_flag === 'SPECTRUM_1D');
-        return getHost() + '/download-file/' + this.datasetName + '/' + this.object[spectraAttribute.label];
+        return getHost() + '/download-file/' + this.datasetSelected + '/' + this.object[spectraAttribute.label];
     }
 
     /**
diff --git a/client/src/app/instance/detail/containers/detail.component.html b/client/src/app/instance/detail/containers/detail.component.html
index 3b15ddb5..2899b8cf 100644
--- a/client/src/app/instance/detail/containers/detail.component.html
+++ b/client/src/app/instance/detail/containers/detail.component.html
@@ -6,26 +6,27 @@
             </button>
         </div>
     </div>
-    <div *ngIf="objectIsLoading | async" id="div-spinner" class="row justify-content-center mt-5">
-        <span class="fas fa-circle-notch fa-spin fa-3x"></span>
-        <span class="sr-only">Loading...</span>
-    </div>
-    <div *ngIf="objectIsLoaded | async" [ngSwitch]="getObjectType(attributeList | async)">
+    <app-spinner *ngIf="(attributeListIsLoading | async)
+        || (outputFamilyListIsLoading | async)
+        || (outputCategoryListIsLoading | async)
+        || (objectIsLoading | async)">
+    </app-spinner>
+    <div *ngIf="(attributeListIsLoaded | async) && (outputFamilyListIsLoaded | async) && (outputCategoryListIsLoaded | async) && (objectIsLoaded | async)" [ngSwitch]="getObjectType(attributeList | async)">
         <app-spectra-object *ngSwitchCase="'spectra'"
-            [datasetName]="datasetName | async"
+            [datasetSelected]="datasetSelected | async"
             [outputFamilyList]="outputFamilyList | async"
-            [categoryList]="categoryList | async"
+            [outputCategoryList]="outputCategoryList | async"
             [attributeList]="attributeList | async"
             [object]="object | async"
+            [spectraCSV]="spectraCSV | async"
             [spectraIsLoading]="spectraIsLoading | async"
             [spectraIsLoaded]="spectraIsLoaded | async"
-            [spectraCSV]="spectraCSV | async"
             (getSpectraCSV)="getSpectraCSV($event)">
         </app-spectra-object>
         <app-default-object *ngSwitchDefault
-            [datasetName]="datasetName | async"
+            [datasetSelected]="datasetSelected | async"
             [outputFamilyList]="outputFamilyList | async"
-            [categoryList]="categoryList | async"
+            [outputCategoryList]="outputCategoryList | async"
             [attributeList]="attributeList | async"
             [object]="object | async">
         </app-default-object>
diff --git a/client/src/app/instance/detail/containers/detail.component.ts b/client/src/app/instance/detail/containers/detail.component.ts
index b1968024..331290c2 100644
--- a/client/src/app/instance/detail/containers/detail.component.ts
+++ b/client/src/app/instance/detail/containers/detail.component.ts
@@ -11,13 +11,20 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Location } from '@angular/common';
 
 import { Store } from '@ngrx/store';
-import { Observable } from 'rxjs';
+import { Observable, Subscription } from 'rxjs';
 
-import * as fromDetail from '../store/detail.reducer';
-import * as detailActions from '../store/detail.action';
-import * as detailSelector from '../store/detail.selector';
-import * as metamodelSelector from '../../metamodel/selectors';
-import { Attribute, Category, Family } from '../../metamodel/model';
+import { Attribute, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
+import * as fromDetail from 'src/app/instance/store/reducers/detail.reducer';
+import * as detailActions from 'src/app/instance/store/actions/detail.actions';
+import * as detailSelector from 'src/app/instance/store/selectors/detail.selector';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+import * as searchSelector from 'src/app/instance/store/selectors/search.selector';
+import * as outputFamilyActions from 'src/app/metamodel/actions/output-family.actions';
+import * as outputFamilySelector from 'src/app/metamodel/selectors/output-family.selector';
+import * as outputCategoryActions from 'src/app/metamodel/actions/output-category.actions';
+import * as outputCategorySelector from 'src/app/metamodel/selectors/output-category.selector';
+import * as attributeActions from 'src/app/metamodel/actions/attribute.actions';
+import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
 
 /**
  * Interface for store state.
@@ -40,38 +47,55 @@ interface StoreState {
  * @implements OnDestroy
  */
 export class DetailComponent implements OnInit, OnDestroy {
+    public datasetSelected: Observable<string>;
     public pristine: Observable<boolean>;
-    public datasetName: Observable<string>;
     public attributeList: Observable<Attribute[]>;
-    public outputFamilyList: Observable<Family[]>;
-    public categoryList: Observable<Category[]>;
+    public attributeListIsLoading: Observable<boolean>;
+    public attributeListIsLoaded: Observable<boolean>;
+    public outputFamilyList: Observable<OutputFamily[]>;
+    public outputFamilyListIsLoading: Observable<boolean>;
+    public outputFamilyListIsLoaded: Observable<boolean>;
+    public outputCategoryList: Observable<OutputCategory[]>;
+    public outputCategoryListIsLoading: Observable<boolean>;
+    public outputCategoryListIsLoaded: Observable<boolean>;
+    public object: Observable<any>;
     public objectIsLoading: Observable<boolean>;
     public objectIsLoaded: Observable<boolean>;
-    public object: Observable<any>;
+    public spectraCSV: Observable<string>;
     public spectraIsLoading: Observable<boolean>;
     public spectraIsLoaded: Observable<boolean>;
-    public spectraCSV: Observable<string>;
+
+    private attributeListIsLoadedSubscription: Subscription;
 
     constructor(private location: Location, private store: Store<StoreState>) {
-        this.pristine = this.store.select(detailSelector.getSearchIsPristine);
-        this.datasetName = this.store.select(detailSelector.getDatasetName);
-        this.outputFamilyList = this.store.select(metamodelSelector.getOutputFamilyList);
-        this.categoryList = this.store.select(metamodelSelector.getCategoryList);
-        this.objectIsLoading = this.store.select(detailSelector.getObjectIsLoading);
-        this.objectIsLoaded = this.store.select(detailSelector.getObjectIsLoaded);
-        this.object = this.store.select(detailSelector.getObject);
-        this.spectraIsLoading = this.store.select(detailSelector.getSpectraIsLoading);
-        this.spectraIsLoaded = this.store.select(detailSelector.getSpectraIsLoaded);
-        this.spectraCSV = this.store.select(detailSelector.getSpectraCSV);
+        this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute);
+        this.pristine = store.select(searchSelector.selectPristine);
+        this.attributeList = store.select(attributeSelector.selectAllAttributes);
+        this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading);
+        this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded);
+        this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilies);
+        this.outputFamilyListIsLoading = store.select(outputFamilySelector.selectOutputFamilyListIsLoading);
+        this.outputFamilyListIsLoaded = store.select(outputFamilySelector.selectOutputFamilyListIsLoaded);
+        this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategories);
+        this.outputCategoryListIsLoading = store.select(outputCategorySelector.selectOutputCategoryListIsLoading);
+        this.outputCategoryListIsLoaded = store.select(outputCategorySelector.selectOutputCategoryListIsLoaded);
+        this.object = this.store.select(detailSelector.selectObject);
+        this.objectIsLoading = this.store.select(detailSelector.selectObjectIsLoading);
+        this.objectIsLoaded = this.store.select(detailSelector.selectObjectIsLoaded);
+        this.spectraCSV = this.store.select(detailSelector.selectSpectraCSV);
+        this.spectraIsLoading = this.store.select(detailSelector.selectObjectIsLoading);
+        this.spectraIsLoaded = this.store.select(detailSelector.selectSpectraIsLoaded);
     }
 
     ngOnInit() {
         // Create a micro task that is processed after the current synchronous code
         // This micro task prevent the expression has changed after view init error
-        Promise.resolve(null).then(() => this.store.dispatch(new detailActions.InitDetailAction()));
-        this.datasetName.subscribe(datasetName => {
-            if (datasetName) {
-                this.attributeList = this.store.select(metamodelSelector.getAttributeList, { dname: datasetName });
+        Promise.resolve(null).then(() => this.store.dispatch(attributeActions.loadAttributeList()));
+        Promise.resolve(null).then(() => this.store.dispatch(outputFamilyActions.loadOutputFamilyList()));
+        Promise.resolve(null).then(() => this.store.dispatch(outputCategoryActions.loadOutputCategoryList()));
+        this.attributeListIsLoadedSubscription = this.attributeListIsLoaded.subscribe(attributeListIsLoaded => {
+            if (attributeListIsLoaded) {
+                Promise.resolve(null).then(() => this.store.dispatch(detailActions.retrieveObject()));
             }
         });
     }
@@ -84,7 +108,7 @@ export class DetailComponent implements OnInit, OnDestroy {
      * @return string
      */
     getObjectType(attributeList: Attribute[]): string {
-        const spectrumAttribute: Attribute =  attributeList
+        const spectrumAttribute: Attribute = attributeList
             .filter(a => a.detail)
             .find(attribute => attribute.search_flag === 'SPECTRUM_1D');
         if (spectrumAttribute) {
@@ -106,13 +130,14 @@ export class DetailComponent implements OnInit, OnDestroy {
      * @param  {string} spectraFile - The spectra file name.
      */
     getSpectraCSV(spectraFile: string): void {
-        this.store.dispatch(new detailActions.RetrieveSpectraAction(spectraFile));
+        this.store.dispatch(detailActions.retrieveSpectra({ filename: spectraFile }));
     }
 
     /**
      * Resets detail information.
      */
     ngOnDestroy() {
-        this.store.dispatch(new detailActions.DestroyDetailAction());
+        this.attributeListIsLoadedSubscription.unsubscribe();
+        // this.store.dispatch(new detailActions.DestroyDetailAction());
     }
 }
diff --git a/client/src/app/instance/detail/detail-routing.module.ts b/client/src/app/instance/detail/detail-routing.module.ts
index ee130892..8f88b99b 100644
--- a/client/src/app/instance/detail/detail-routing.module.ts
+++ b/client/src/app/instance/detail/detail-routing.module.ts
@@ -10,10 +10,11 @@
 import { NgModule } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
 
-//import { DetailComponent } from './detail.component';
+import { DetailComponent } from './containers/detail.component';
 
 const routes: Routes = [
-    //{ path: '', component: DetailComponent }
+    { path: '', redirectTo: ':/dname/:id', pathMatch: 'full' },
+    { path: ':dname/:id', component: DetailComponent }
 ];
 
 @NgModule({
@@ -23,5 +24,5 @@ const routes: Routes = [
 export class DetailRoutingModule { }
 
 export const routedComponents = [
-    //DetailComponent
+    DetailComponent
 ];
diff --git a/client/src/app/instance/detail/detail.module.ts b/client/src/app/instance/detail/detail.module.ts
index 7e1605ce..fbaaabfa 100644
--- a/client/src/app/instance/detail/detail.module.ts
+++ b/client/src/app/instance/detail/detail.module.ts
@@ -11,12 +11,16 @@ import { NgModule } from '@angular/core';
 
 import { SharedModule } from 'src/app/shared/shared.module';
 import { DetailRoutingModule, routedComponents } from './detail-routing.module';
+import { dummiesComponents } from './components';
 
 @NgModule({
     imports: [
         SharedModule,
         DetailRoutingModule
     ],
-    declarations: [routedComponents]
+    declarations: [
+        routedComponents,
+        dummiesComponents
+    ]
 })
 export class DetailModule { }
diff --git a/client/src/app/instance/instance-routing.module.ts b/client/src/app/instance/instance-routing.module.ts
index 37baa107..f893d072 100644
--- a/client/src/app/instance/instance-routing.module.ts
+++ b/client/src/app/instance/instance-routing.module.ts
@@ -18,7 +18,8 @@ const routes: Routes = [
             { path: '', redirectTo: 'home', pathMatch: 'full' },
             { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
             { path: 'documentation', loadChildren: () => import('./documentation/documentation.module').then(m => m.DocumentationModule) },
-            { path: 'search', loadChildren: () => import('./search/search.module').then(m => m.SearchModule) }
+            { path: 'search', loadChildren: () => import('./search/search.module').then(m => m.SearchModule) },
+            { path: 'detail', loadChildren: () => import('./detail/detail.module').then(m => m.DetailModule) }
         ]
     }
 ];
diff --git a/client/src/app/instance/instance.reducer.ts b/client/src/app/instance/instance.reducer.ts
index f4454161..f660ab9a 100644
--- a/client/src/app/instance/instance.reducer.ts
+++ b/client/src/app/instance/instance.reducer.ts
@@ -14,19 +14,22 @@ import * as metamodel from './store/reducers/metamodel.reducer';
 import * as search from './store/reducers/search.reducer';
 import * as samp from './store/reducers/samp.reducer';
 import * as coneSearch from './store/reducers/cone-search.reducer';
+import * as detail from './store/reducers/detail.reducer';
 
 export interface State {
     metamodel: metamodel.State,
     search: search.State,
     samp: samp.State,
     coneSearch: coneSearch.State
+    detail: detail.State
 }
 
 const reducers = {
     metamodel: metamodel.metamodelReducer,
     search: search.searchReducer,
     samp: samp.sampReducer,
-    coneSearch: coneSearch.coneSearchReducer
+    coneSearch: coneSearch.coneSearchReducer,
+    detail: detail.detailReducer
 };
 
 export const instanceReducer = combineReducers(reducers);
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.html b/client/src/app/instance/search/components/result/datatable-tab.component.html
index 66316d7a..26dd36d1 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.html
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.html
@@ -10,6 +10,7 @@
         </button>
         <app-datatable
                 [dataset]="datasetList | datasetByName:datasetSelected"
+                [instanceSelected]="instanceSelected"
                 [attributeList]="attributeList"
                 [outputList]="outputList"
                 [dataLength]="dataLength"
diff --git a/client/src/app/instance/search/components/result/datatable-tab.component.ts b/client/src/app/instance/search/components/result/datatable-tab.component.ts
index c2d7dd1d..c24e1979 100644
--- a/client/src/app/instance/search/components/result/datatable-tab.component.ts
+++ b/client/src/app/instance/search/components/result/datatable-tab.component.ts
@@ -23,6 +23,7 @@ import { Pagination } from 'src/app/instance/store/models';
  */
 export class DatatableTabComponent {
     @Input() datasetSelected: string;
+    @Input() instanceSelected: string;
     @Input() datasetList: Dataset[];
     @Input() attributeList: Attribute[];
     @Input() outputList: number[];
diff --git a/client/src/app/instance/search/containers/result.component.html b/client/src/app/instance/search/containers/result.component.html
index ab045ae2..9e4fec28 100644
--- a/client/src/app/instance/search/containers/result.component.html
+++ b/client/src/app/instance/search/containers/result.component.html
@@ -64,6 +64,7 @@
             </app-cone-search-plot-tab> -->
             <app-datatable-tab
                 [datasetSelected]="datasetSelected | async"
+                [instanceSelected]="instanceSelected | async"
                 [datasetList]="datasetList | async"
                 [attributeList]="attributeList | async"
                 [outputList]="outputList | async"
diff --git a/client/src/app/instance/shared-search/components/datatable/datatable.component.html b/client/src/app/instance/shared-search/components/datatable/datatable.component.html
index 8a373adc..881f7906 100644
--- a/client/src/app/instance/shared-search/components/datatable/datatable.component.html
+++ b/client/src/app/instance/shared-search/components/datatable/datatable.component.html
@@ -45,6 +45,7 @@
                             <app-detail-renderer
                                 [value]="datum[attribute.label]"
                                 [datasetName]="dataset.name"
+                                [instanceSelected]="instanceSelected"
                                 [config]="getRendererConfig(attribute)">
                             </app-detail-renderer>
                         </div>
diff --git a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
index 56d115ee..698cf040 100644
--- a/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/datatable.component.ts
@@ -26,6 +26,7 @@ import { Pagination, PaginationOrder } from 'src/app/instance/store/models';
  */
 export class DatatableComponent implements OnInit {
     @Input() dataset: Dataset;
+    @Input() instanceSelected: string;
     @Input() attributeList: Attribute[];
     @Input() outputList: number[];
     @Input() dataLength: number;
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html
index 0b6336b1..37c9f313 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.html
@@ -1,4 +1,4 @@
-<a routerLink="/detail/{{ datasetName }}/{{ value }}" [target]="config.blank == true ? '_blank' : '_self'"
+<a routerLink="/instance/{{ instanceSelected }}/detail/{{ datasetName }}/{{ value }}" [target]="config.blank == true ? '_blank' : '_self'"
     [ngClass]="{'btn btn-outline-primary btn-sm' : config.display == 'text-button'}">
     {{ value }}
 </a>
diff --git a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts
index 06411ef9..d6df4dfd 100644
--- a/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts
+++ b/client/src/app/instance/shared-search/components/datatable/renderer/detail-renderer.component.ts
@@ -23,5 +23,6 @@ import { DetailRendererConfig } from 'src/app/metamodel/models/renderers/detail-
 export class DetailRendererComponent {
     @Input() value: string | number;
     @Input() datasetName: string;
+    @Input() instanceSelected: string;
     @Input() config: DetailRendererConfig;
 }
diff --git a/client/src/app/instance/store/actions/detail.actions.ts b/client/src/app/instance/store/actions/detail.actions.ts
new file mode 100644
index 00000000..c88210bc
--- /dev/null
+++ b/client/src/app/instance/store/actions/detail.actions.ts
@@ -0,0 +1,17 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createAction, props } from '@ngrx/store';
+
+export const retrieveObject = createAction('[Detail] Retrieve Object');
+export const retrieveObjectSuccess = createAction('[Detail] Retrieve Object Success', props<{ object: any }>());
+export const retrieveObjectFail = createAction('[Detail] Retrieve Object Fail');
+export const retrieveSpectra = createAction('[Detail] Retrieve Spectra', props<{ filename: string }>());
+export const retrieveSpectraSuccess = createAction('[Detail] Retrieve Spectra Success', props<{ spectraCSV: string }>());
+export const retrieveSpectraFail = createAction('[Detail] Retrieve Spectra Fail');
diff --git a/client/src/app/instance/store/effects/detail.effects.ts b/client/src/app/instance/store/effects/detail.effects.ts
new file mode 100644
index 00000000..256412ae
--- /dev/null
+++ b/client/src/app/instance/store/effects/detail.effects.ts
@@ -0,0 +1,80 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Injectable } from '@angular/core';
+
+import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
+import { Store  } from '@ngrx/store';
+import { of } from 'rxjs';
+import { map, tap, mergeMap, catchError } from 'rxjs/operators';
+import { ToastrService } from 'ngx-toastr';
+
+import { DetailService } from '../services/detail.service';
+import * as detailActions from '../actions/detail.actions';
+import * as detailSelector from '../selectors/detail.selector';
+import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+ 
+@Injectable()
+export class DetailEffects {
+    retrieveObject$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailActions.retrieveObject),
+            concatLatestFrom(() => [
+                this.store.select(datasetSelector.selectDatasetNameByRoute),
+                this.store.select(attributeSelector.selectAllAttributes),
+                this.store.select(detailSelector.selectIdByRoute)
+            ]),
+            mergeMap(([action, datasetName, attributeList, id]) => this.detailService.retrieveObject(
+                datasetName,
+                attributeList.find(attribute => attribute.order_by).id,
+                id,
+                attributeList.filter(attribute => attribute.detail).map(attribute => attribute.id)
+            ).pipe(
+                map(object => detailActions.retrieveObjectSuccess({ object: object[0] })),
+                catchError(() => of(detailActions.retrieveObjectFail()))
+            ))
+        )
+    );
+
+    retrieveObjectFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(detailActions.retrieveObjectFail),
+            tap(() => this.toastr.error('Loading Failed!', 'Unable to load the object'))
+        ), { dispatch: false}
+    );
+
+    retrieveSpectra$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(detailActions.retrieveSpectra),
+            concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
+            mergeMap(([action, datasetName]) => this.detailService.retrieveSpectra(
+                datasetName,
+                action.filename
+            ).pipe(
+                map(spectraCSV => detailActions.retrieveSpectraSuccess({ spectraCSV })),
+                catchError(() => of(detailActions.retrieveSpectraFail()))
+            ))
+        )
+    );
+
+    retrieveSpectraFail$ = createEffect(() => 
+        this.actions$.pipe(
+            ofType(detailActions.retrieveSpectraFail),
+            tap(() => this.toastr.error('Loading Failed!', 'Unable to load spectra'))
+        ), { dispatch: false}
+    );
+    
+    constructor(
+        private actions$: Actions,
+        private detailService: DetailService,
+        private store: Store<{ }>,
+        private toastr: ToastrService
+    ) {}
+}
diff --git a/client/src/app/instance/store/effects/index.ts b/client/src/app/instance/store/effects/index.ts
index 3d2ecd77..29c6402a 100644
--- a/client/src/app/instance/store/effects/index.ts
+++ b/client/src/app/instance/store/effects/index.ts
@@ -2,10 +2,12 @@ import { MetamodelEffects } from './metamodel.effects';
 import { SampEffects } from './samp.effects';
 import { SearchEffects } from './search.effects';
 import { ConeSearchEffects } from './cone-search.effects';
+import { DetailEffects } from './detail.effects';
 
 export const instanceEffects = [
     MetamodelEffects,
     SampEffects,
     SearchEffects,
-    ConeSearchEffects
+    ConeSearchEffects,
+    DetailEffects
 ];
diff --git a/client/src/app/instance/store/reducers/detail.reducer.ts b/client/src/app/instance/store/reducers/detail.reducer.ts
new file mode 100644
index 00000000..466fcfc1
--- /dev/null
+++ b/client/src/app/instance/store/reducers/detail.reducer.ts
@@ -0,0 +1,71 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createReducer, on } from '@ngrx/store';
+
+import * as detailActions from '../actions/detail.actions';
+
+export interface State {
+    object: any;
+    objectIsLoading: boolean;
+    objectIsLoaded: boolean;
+    spectraCSV: string;
+    spectraIsLoading: boolean;
+    spectraIsLoaded: boolean;
+}
+
+export const initialState: State = {
+    object: null,
+    objectIsLoading: false,
+    objectIsLoaded: false,
+    spectraCSV: null,
+    spectraIsLoading: false,
+    spectraIsLoaded: false,
+};
+
+export const detailReducer = createReducer(
+    initialState,
+    on(detailActions.retrieveObject, state => ({
+        ...state,
+        objectIsLoading: true,
+        objectIsLoaded: false
+    })),
+    on(detailActions.retrieveObjectSuccess, (state, { object }) => ({
+        ...state,
+        objectIsLoading: false,
+        objectIsLoaded: true,
+        object
+    })),
+    on(detailActions.retrieveObjectFail, state => ({
+        ...state,
+        objectIsLoading: false
+    })),
+    on(detailActions.retrieveSpectra, state => ({
+        ...state,
+        spectraIsLoading: true,
+        spectraIsLoaded: false
+    })),
+    on(detailActions.retrieveSpectraSuccess, (state, { spectraCSV }) => ({
+        ...state,
+        spectraIsLoading: false,
+        spectraIsLoaded: true,
+        spectraCSV
+    })),
+    on(detailActions.retrieveSpectraFail, state => ({
+        ...state,
+        spectraIsLoading: false
+    }))
+);
+
+export const selectObject = (state: State) => state.object;
+export const selectObjectIsLoading = (state: State) => state.objectIsLoading;
+export const selectObjectIsLoaded = (state: State) => state.objectIsLoaded;
+export const selectSpectraCSV = (state: State) => state.spectraCSV;
+export const selectSpectraIsLoading = (state: State) => state.spectraIsLoading;
+export const selectSpectraIsLoaded = (state: State) => state.spectraIsLoaded;
diff --git a/client/src/app/instance/store/selectors/detail.selector.ts b/client/src/app/instance/store/selectors/detail.selector.ts
new file mode 100644
index 00000000..2fe62d57
--- /dev/null
+++ b/client/src/app/instance/store/selectors/detail.selector.ts
@@ -0,0 +1,53 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { createSelector } from '@ngrx/store';
+
+import * as reducer from '../../instance.reducer';
+import * as fromDetail from '../reducers/detail.reducer';
+
+export const selectDetailState = createSelector(
+    reducer.getInstanceState,
+    (state: reducer.State) => state.detail
+);
+
+export const selectObject = createSelector(
+    selectDetailState,
+    fromDetail.selectObject
+);
+
+export const selectObjectIsLoading = createSelector(
+    selectDetailState,
+    fromDetail.selectObjectIsLoading
+);
+
+export const selectObjectIsLoaded = createSelector(
+    selectDetailState,
+    fromDetail.selectObjectIsLoaded
+);
+
+export const selectSpectraCSV = createSelector(
+    selectDetailState,
+    fromDetail.selectSpectraCSV
+);
+
+export const selectSpectraIsLoading = createSelector(
+    selectDetailState,
+    fromDetail.selectSpectraIsLoading
+);
+
+export const selectSpectraIsLoaded = createSelector(
+    selectDetailState,
+    fromDetail.selectSpectraIsLoaded
+);
+
+export const selectIdByRoute = createSelector(
+    reducer.selectRouterState,
+    router => router.state.params.id
+);
diff --git a/client/src/app/instance/store/selectors/search.selector.ts b/client/src/app/instance/store/selectors/search.selector.ts
index 094c3673..44ebe388 100644
--- a/client/src/app/instance/store/selectors/search.selector.ts
+++ b/client/src/app/instance/store/selectors/search.selector.ts
@@ -14,88 +14,88 @@ import * as reducer from '../../instance.reducer';
 import * as fromSearch from '../reducers/search.reducer';
 import * as coneSearchSelector from './cone-search.selector';
 
-export const selectInstanceState = createSelector(
+export const selectSearchState = createSelector(
     reducer.getInstanceState,
     (state: reducer.State) => state.search
 );
 
 export const selectPristine = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectPristine
 );
 
 export const selectCurrentDataset = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCurrentDataset
 );
 
 export const selectCurrentStep = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCurrentStep
 );
 
 export const selectCriteriaStepChecked = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCriteriaStepChecked
 );
 
 export const selectOutputStepChecked = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectOutputStepChecked
 );
 
 export const selectResultStepChecked = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectResultStepChecked
 );
 
 export const selectIsConeSearchAdded = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectIsConeSearchAdded
 );
 
 export const selectCriteriaList = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectCriteriaList
 );
 
 export const selectOutputList = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectOutputList
 );
 
 export const selectDataLengthIsLoading = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataLengthIsLoading
 );
 
 export const selectDataLengthIsLoaded = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataLengthIsLoaded
 );
 
 export const selectDataLength = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataLength
 );
 
 export const selectDataIsLoading = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataIsLoading
 );
 
 export const selectDataIsLoaded = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectDataIsLoaded
 );
 
 export const selectData = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectData
 );
 
 export const selectSelectedData = createSelector(
-    selectInstanceState,
+    selectSearchState,
     fromSearch.selectSelectedData
 );
 
diff --git a/client/src/app/instance/store/services/detail.service.ts b/client/src/app/instance/store/services/detail.service.ts
new file mode 100644
index 00000000..e1b5db35
--- /dev/null
+++ b/client/src/app/instance/store/services/detail.service.ts
@@ -0,0 +1,53 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+
+import { Observable } from 'rxjs';
+
+import { environment } from 'src/environments/environment';
+
+@Injectable()
+/**
+ * @class
+ * @classdesc Detail service.
+ */
+export class DetailService {
+    private API_PATH: string = environment.apiUrl + '/search/';
+    private SERVICES_PATH: string = environment.servicesUrl;
+
+    constructor(private http: HttpClient) { }
+
+    /**
+     * Retrieves object details for the given parameters.
+     *
+     * @param  {string} dname - The dataset name.
+     * @param  {number} criterionId - The criterion ID.
+     * @param  {string} objectSelected - The selected object ID.
+     * @param  {number[]} outputList - The output list.
+     *
+     * @return Observable<any[]>
+     */
+    retrieveObject(dname: string, criterionId: number, objectSelected: string, outputList: number[]): Observable<any[]> {
+        const query = dname + '?c=' + criterionId + '::eq::' + objectSelected + '&a=' + outputList.join(';');
+        return this.http.get<any[]>(this.API_PATH + query);
+    }
+
+    /**
+     * Retrieves object details for the given parameters.
+     *
+     * @param  {string} spectraFile - The spectra file name.
+     *
+     * @return Observable<string>
+     */
+    retrieveSpectra(dname: string, spectraFile: string): Observable<string> {
+        return this.http.get(this.SERVICES_PATH + '/spectra-to-csv/' + dname + '?filename=' + spectraFile, { responseType: 'text' });
+    }
+}
diff --git a/client/src/app/instance/store/services/index.ts b/client/src/app/instance/store/services/index.ts
index 4707c60a..cabc0796 100644
--- a/client/src/app/instance/store/services/index.ts
+++ b/client/src/app/instance/store/services/index.ts
@@ -1,9 +1,11 @@
 import { SearchService } from './search.service';
 import { SampService } from './samp.service';
 import { ConeSearchService } from './cone-search.service';
+import { DetailService } from './detail.service';
 
 export const instanceServices = [
     SearchService,
     SampService,
-    ConeSearchService
+    ConeSearchService,
+    DetailService
 ];
diff --git a/client/yarn.lock b/client/yarn.lock
index 2a005f93..374022b1 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -1344,6 +1344,222 @@
   resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4"
   integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==
 
+"@types/d3-array@^1":
+  version "1.2.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.9.tgz#c7dc78992cd8ca5c850243a265fd257ea56df1fa"
+  integrity sha512-E/7RgPr2ylT5dWG0CswMi9NpFcjIEDqLcUSBgNHe/EMahfqYaTx4zhcggG3khqoEB/leY4Vl6nTSbwLUPjXceA==
+
+"@types/d3-axis@^1":
+  version "1.0.16"
+  resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-1.0.16.tgz#93d7a28795c2f8b0e2fd550fcc4d29b7f174e693"
+  integrity sha512-p7085weOmo4W+DzlRRVC/7OI/jugaKbVa6WMQGCQscaMylcbuaVEGk7abJLNyGVFLeCBNrHTdDiqRGnzvL0nXQ==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-brush@^1":
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-1.1.5.tgz#c7cfb58decbfd53ad3e47f0376345e3640a68186"
+  integrity sha512-4zGkBafJf5zCsBtLtvDj/pNMo5X9+Ii/1hUz0GvQ+wEwelUBm2AbIDAzJnp2hLDFF307o0fhxmmocHclhXC+tw==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-chord@^1":
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-1.0.11.tgz#5760765db1b1a4b936c0d9355a821dde9dd25da2"
+  integrity sha512-0DdfJ//bxyW3G9Nefwq/LDgazSKNN8NU0lBT3Cza6uVuInC2awMNsAcv1oKyRFLn9z7kXClH5XjwpveZjuz2eg==
+
+"@types/d3-collection@*":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/d3-collection/-/d3-collection-1.0.10.tgz#bca161e336156968f267c077f7f2bfa8ff224e58"
+  integrity sha512-54Fdv8u5JbuXymtmXm2SYzi1x/Svt+jfWBU5junkhrCewL92VjqtCBDn97coBRVwVFmYNnVTNDyV8gQyPYfm+A==
+
+"@types/d3-color@^1":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.4.2.tgz#944f281d04a0f06e134ea96adbb68303515b2784"
+  integrity sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA==
+
+"@types/d3-contour@^1":
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-1.3.3.tgz#44529d498bbc1db78b195d75e1c9bb889edd647a"
+  integrity sha512-LxwmGIfVJIc1cKs7ZFRQ1FbtXpfH7QTXYRdMIJsFP71uCMdF6jJ0XZakYDX6Hn4yZkLf+7V8FgD34yCcok+5Ww==
+  dependencies:
+    "@types/d3-array" "^1"
+    "@types/geojson" "*"
+
+"@types/d3-dispatch@^1":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-1.0.9.tgz#c5a180f1e251de853b399cfbfbb6dd7f8bf842ae"
+  integrity sha512-zJ44YgjqALmyps+II7b1mZLhrtfV/FOxw9owT87mrweGWcg+WK5oiJX2M3SYJ0XUAExBduarysfgbR11YxzojQ==
+
+"@types/d3-drag@^1":
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-1.2.5.tgz#0b1b852cb41577075aa625ae6149379ea6c34dfd"
+  integrity sha512-7NeTnfolst1Js3Vs7myctBkmJWu6DMI3k597AaHUX98saHjHWJ6vouT83UrpE+xfbSceHV+8A0JgxuwgqgmqWw==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-dsv@^1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-1.2.1.tgz#1524fee9f19d689c2f76aa0e24e230762bf96994"
+  integrity sha512-LLmJmjiqp/fTNEdij5bIwUJ6P6TVNk5hKM9/uk5RPO2YNgEu9XvKO0dJ7Iqd3psEdmZN1m7gB1bOsjr4HmO2BA==
+
+"@types/d3-ease@^1":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-1.0.10.tgz#09910e8558439b6038a7ed620650e510394ffa6d"
+  integrity sha512-fMFTCzd8DOwruE9zlu2O8ci5ct+U5jkGcDS+cH+HCidnJlDs0MZ+TuSVCFtEzh4E5MasItwy+HvgoFtxPHa5Cw==
+
+"@types/d3-fetch@^1":
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-1.2.2.tgz#b93bfe248b8b761af82f4dac57959c989f67da3e"
+  integrity sha512-rtFs92GugtV/NpiJQd0WsmGLcg52tIL0uF0bKbbJg231pR9JEb6HT4AUwrtuLq3lOeKdLBhsjV14qb0pMmd0Aw==
+  dependencies:
+    "@types/d3-dsv" "^1"
+
+"@types/d3-force@^1":
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-1.2.4.tgz#6e274c72288c2db08fbdb8f5b87b9aa83e55a9e8"
+  integrity sha512-fkorLTKvt6AQbFBQwn4aq7h9rJ4c7ZVcPMGB8X6eFFveAyMZcv7t7m6wgF4Eg93rkPgPORU7sAho1QSHNcZu6w==
+
+"@types/d3-format@^1":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.4.2.tgz#ea17bf559b71d9afd569ae9bfe4c544dab863baa"
+  integrity sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ==
+
+"@types/d3-geo@^1":
+  version "1.12.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-1.12.3.tgz#512ebe735cb1cdf5f87ad59608416e2e9e868c5a"
+  integrity sha512-yZbPb7/5DyL/pXkeOmZ7L5ySpuGr4H48t1cuALjnJy5sXQqmSSAYBiwa6Ya/XpWKX2rJqGDDubmh3nOaopOpeA==
+  dependencies:
+    "@types/geojson" "*"
+
+"@types/d3-hierarchy@^1":
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz#50657f420d565a06c0b950a4b82eee0a369f2dea"
+  integrity sha512-AbStKxNyWiMDQPGDguG2Kuhlq1Sv539pZSxYbx4UZeYkutpPwXCcgyiRrlV4YH64nIOsKx7XVnOMy9O7rJsXkg==
+
+"@types/d3-interpolate@^1":
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz#88902a205f682773a517612299a44699285eed7b"
+  integrity sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==
+  dependencies:
+    "@types/d3-color" "^1"
+
+"@types/d3-path@^1":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c"
+  integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==
+
+"@types/d3-polygon@^1":
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-1.0.8.tgz#127ee83fccda5bf57384011da90f31367fea1530"
+  integrity sha512-1TOJPXCBJC9V3+K3tGbTqD/CsqLyv/YkTXAcwdsZzxqw5cvpdnCuDl42M4Dvi8XzMxZNCT9pL4ibrK2n4VmAcw==
+
+"@types/d3-quadtree@^1":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-1.0.9.tgz#c7c3b795b5af06e5b043d1d34e754a434b3bae59"
+  integrity sha512-5E0OJJn2QVavITFEc1AQlI8gLcIoDZcTKOD3feKFckQVmFV4CXhqRFt83tYNVNIN4ZzRkjlAMavJa1ldMhf5rA==
+
+"@types/d3-random@^1":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-1.1.3.tgz#8f7fdc23f92d1561e0694eb49567e8ab50537a19"
+  integrity sha512-XXR+ZbFCoOd4peXSMYJzwk0/elP37WWAzS/DG+90eilzVbUSsgKhBcWqylGWe+lA2ubgr7afWAOBaBxRgMUrBQ==
+
+"@types/d3-scale-chromatic@^1":
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.5.1.tgz#e2b7c3401e5c13809f831911eb820e444f4fc67a"
+  integrity sha512-7FtJYrmXTEWLykShjYhoGuDNR/Bda0+tstZMkFj4RRxUEryv16AGh3be21tqg84B6KfEwiZyEpBcTyPyU+GWjg==
+
+"@types/d3-scale@^2":
+  version "2.2.6"
+  resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-2.2.6.tgz#28540b4dfc99d978970e873e4138a6bea2ea6ab8"
+  integrity sha512-CHu34T5bGrJOeuhGxyiz9Xvaa9PlsIaQoOqjDg7zqeGj2x0rwPhGquiy03unigvcMxmvY0hEaAouT0LOFTLpIw==
+  dependencies:
+    "@types/d3-time" "^1"
+
+"@types/d3-selection@^1":
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-1.4.3.tgz#36928bbe64eb8e0bbcbaa01fb05c21ff6c71fa93"
+  integrity sha512-GjKQWVZO6Sa96HiKO6R93VBE8DUW+DDkFpIMf9vpY5S78qZTlRRSNUsHr/afDpF7TvLDV7VxrUFOWW7vdIlYkA==
+
+"@types/d3-shape@^1":
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.8.tgz#c3c15ec7436b4ce24e38de517586850f1fea8e89"
+  integrity sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==
+  dependencies:
+    "@types/d3-path" "^1"
+
+"@types/d3-time-format@^2":
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.3.1.tgz#87a30e4513b9d1d53b920327a361f87255bf3372"
+  integrity sha512-fck0Z9RGfIQn3GJIEKVrp15h9m6Vlg0d5XXeiE/6+CQiBmMDZxfR21XtjEPuDeg7gC3bBM0SdieA5XF3GW1wKA==
+
+"@types/d3-time@^1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.1.1.tgz#6cf3a4242c3bbac00440dfb8ba7884f16bedfcbf"
+  integrity sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw==
+
+"@types/d3-timer@^1":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-1.0.10.tgz#329c51c2c931f44ed0acff78b8c84571acf0ed21"
+  integrity sha512-ZnAbquVqy+4ZjdW0cY6URp+qF/AzTVNda2jYyOzpR2cPT35FTXl78s15Bomph9+ckOiI1TtkljnWkwbIGAb6rg==
+
+"@types/d3-transition@^1":
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-1.3.2.tgz#ed59beca7b4d679cfa52f88a6a50e5bbeb7e0a3c"
+  integrity sha512-J+a3SuF/E7wXbOSN19p8ZieQSFIm5hU2Egqtndbc54LXaAEOpLfDx4sBu/PKAKzHOdgKK1wkMhINKqNh4aoZAg==
+  dependencies:
+    "@types/d3-selection" "^1"
+
+"@types/d3-voronoi@*":
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz#7bbc210818a3a5c5e0bafb051420df206617c9e5"
+  integrity sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==
+
+"@types/d3-zoom@^1":
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-1.8.3.tgz#00237900c6fdc2bb4fe82679ee4d74eb8fbe7b3c"
+  integrity sha512-3kHkL6sPiDdbfGhzlp5gIHyu3kULhtnHTTAl3UBZVtWB1PzcLL8vdmz5mTx7plLiUqOA2Y+yT2GKjt/TdA2p7Q==
+  dependencies:
+    "@types/d3-interpolate" "^1"
+    "@types/d3-selection" "^1"
+
+"@types/d3@^5.7.2":
+  version "5.16.4"
+  resolved "https://registry.yarnpkg.com/@types/d3/-/d3-5.16.4.tgz#a7dc24a3dc1c19922eee72ba16144fd5bcea987a"
+  integrity sha512-2u0O9iP1MubFiQ+AhR1id4Egs+07BLtvRATG6IL2Gs9+KzdrfaxCKNq5hxEyw1kxwsqB/lCgr108XuHcKtb/5w==
+  dependencies:
+    "@types/d3-array" "^1"
+    "@types/d3-axis" "^1"
+    "@types/d3-brush" "^1"
+    "@types/d3-chord" "^1"
+    "@types/d3-collection" "*"
+    "@types/d3-color" "^1"
+    "@types/d3-contour" "^1"
+    "@types/d3-dispatch" "^1"
+    "@types/d3-drag" "^1"
+    "@types/d3-dsv" "^1"
+    "@types/d3-ease" "^1"
+    "@types/d3-fetch" "^1"
+    "@types/d3-force" "^1"
+    "@types/d3-format" "^1"
+    "@types/d3-geo" "^1"
+    "@types/d3-hierarchy" "^1"
+    "@types/d3-interpolate" "^1"
+    "@types/d3-path" "^1"
+    "@types/d3-polygon" "^1"
+    "@types/d3-quadtree" "^1"
+    "@types/d3-random" "^1"
+    "@types/d3-scale" "^2"
+    "@types/d3-scale-chromatic" "^1"
+    "@types/d3-selection" "^1"
+    "@types/d3-shape" "^1"
+    "@types/d3-time" "^1"
+    "@types/d3-time-format" "^2"
+    "@types/d3-timer" "^1"
+    "@types/d3-transition" "^1"
+    "@types/d3-voronoi" "*"
+    "@types/d3-zoom" "^1"
+
 "@types/eslint-scope@^3.7.0":
   version "3.7.0"
   resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86"
@@ -1370,6 +1586,11 @@
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
   integrity sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==
 
+"@types/geojson@*":
+  version "7946.0.8"
+  resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
+  integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==
+
 "@types/glob@^7.1.1":
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
@@ -2402,7 +2623,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@^2.20.0:
+commander@2, commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
   integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -2780,6 +3001,254 @@ custom-event@~1.0.0:
   resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
   integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=
 
+d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
+  integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==
+
+d3-axis@1:
+  version "1.0.12"
+  resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9"
+  integrity sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==
+
+d3-brush@1:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.1.6.tgz#b0a22c7372cabec128bdddf9bddc058592f89e9b"
+  integrity sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==
+  dependencies:
+    d3-dispatch "1"
+    d3-drag "1"
+    d3-interpolate "1"
+    d3-selection "1"
+    d3-transition "1"
+
+d3-chord@1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f"
+  integrity sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==
+  dependencies:
+    d3-array "1"
+    d3-path "1"
+
+d3-collection@1:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e"
+  integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==
+
+d3-color@1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a"
+  integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==
+
+d3-contour@1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3"
+  integrity sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==
+  dependencies:
+    d3-array "^1.1.1"
+
+d3-dispatch@1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58"
+  integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==
+
+d3-drag@1:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70"
+  integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==
+  dependencies:
+    d3-dispatch "1"
+    d3-selection "1"
+
+d3-dsv@1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c"
+  integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==
+  dependencies:
+    commander "2"
+    iconv-lite "0.4"
+    rw "1"
+
+d3-ease@1:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2"
+  integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==
+
+d3-fetch@1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.2.0.tgz#15ce2ecfc41b092b1db50abd2c552c2316cf7fc7"
+  integrity sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==
+  dependencies:
+    d3-dsv "1"
+
+d3-force@1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.2.1.tgz#fd29a5d1ff181c9e7f0669e4bd72bdb0e914ec0b"
+  integrity sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==
+  dependencies:
+    d3-collection "1"
+    d3-dispatch "1"
+    d3-quadtree "1"
+    d3-timer "1"
+
+d3-format@1:
+  version "1.4.5"
+  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4"
+  integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==
+
+d3-geo@1:
+  version "1.12.1"
+  resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f"
+  integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==
+  dependencies:
+    d3-array "1"
+
+d3-hierarchy@1:
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83"
+  integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==
+
+d3-interpolate@1:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987"
+  integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==
+  dependencies:
+    d3-color "1"
+
+d3-path@1:
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
+  integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
+
+d3-polygon@1:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.6.tgz#0bf8cb8180a6dc107f518ddf7975e12abbfbd38e"
+  integrity sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==
+
+d3-quadtree@1:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135"
+  integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==
+
+d3-random@1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.2.tgz#2833be7c124360bf9e2d3fd4f33847cfe6cab291"
+  integrity sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==
+
+d3-scale-chromatic@1:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz#54e333fc78212f439b14641fb55801dd81135a98"
+  integrity sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==
+  dependencies:
+    d3-color "1"
+    d3-interpolate "1"
+
+d3-scale@2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f"
+  integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==
+  dependencies:
+    d3-array "^1.2.0"
+    d3-collection "1"
+    d3-format "1"
+    d3-interpolate "1"
+    d3-time "1"
+    d3-time-format "2"
+
+d3-selection@1, d3-selection@^1.1.0:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c"
+  integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==
+
+d3-shape@1:
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
+  integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
+  dependencies:
+    d3-path "1"
+
+d3-time-format@2:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850"
+  integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==
+  dependencies:
+    d3-time "1"
+
+d3-time@1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1"
+  integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
+
+d3-timer@1:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5"
+  integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==
+
+d3-transition@1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398"
+  integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==
+  dependencies:
+    d3-color "1"
+    d3-dispatch "1"
+    d3-ease "1"
+    d3-interpolate "1"
+    d3-selection "^1.1.0"
+    d3-timer "1"
+
+d3-voronoi@1:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297"
+  integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==
+
+d3-zoom@1:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.8.3.tgz#b6a3dbe738c7763121cd05b8a7795ffe17f4fc0a"
+  integrity sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==
+  dependencies:
+    d3-dispatch "1"
+    d3-drag "1"
+    d3-interpolate "1"
+    d3-selection "1"
+    d3-transition "1"
+
+d3@^5.15.1:
+  version "5.16.0"
+  resolved "https://registry.yarnpkg.com/d3/-/d3-5.16.0.tgz#9c5e8d3b56403c79d4ed42fbd62f6113f199c877"
+  integrity sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==
+  dependencies:
+    d3-array "1"
+    d3-axis "1"
+    d3-brush "1"
+    d3-chord "1"
+    d3-collection "1"
+    d3-color "1"
+    d3-contour "1"
+    d3-dispatch "1"
+    d3-drag "1"
+    d3-dsv "1"
+    d3-ease "1"
+    d3-fetch "1"
+    d3-force "1"
+    d3-format "1"
+    d3-geo "1"
+    d3-hierarchy "1"
+    d3-interpolate "1"
+    d3-path "1"
+    d3-polygon "1"
+    d3-quadtree "1"
+    d3-random "1"
+    d3-scale "2"
+    d3-scale-chromatic "1"
+    d3-selection "1"
+    d3-shape "1"
+    d3-time "1"
+    d3-time-format "2"
+    d3-timer "1"
+    d3-transition "1"
+    d3-voronoi "1"
+    d3-zoom "1"
+
 dashdash@^1.12.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -3851,7 +4320,7 @@ humanize-ms@^1.2.1:
   dependencies:
     ms "^2.0.0"
 
-iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
+iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
   version "0.4.24"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
   integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -6618,6 +7087,11 @@ run-parallel@^1.1.9:
   dependencies:
     queue-microtask "^1.2.2"
 
+rw@1:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
+  integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
+
 rxjs@6.6.7, rxjs@^6.6.6, rxjs@~6.6.0:
   version "6.6.7"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
-- 
GitLab