@@ -3,21 +3,21 @@ import { BaseD3, RenderOptions as BaseRenderOptions } from './base.d3';
3
3
import * as topojson from 'topojson' ;
4
4
import { GeometryCollection , Topology } from 'topojson-specification' ;
5
5
import { Observable } from 'rxjs' ;
6
- import { GeoDatum } from '../datasets/queries/geo.query' ;
6
+ import { GeoDatum , TerritoryLevel } from '../datasets/queries/geo.query' ;
7
7
import * as GeoJSON from 'geojson' ;
8
8
import { linearScale } from '../utils/misc' ;
9
9
10
10
type CountryGeometry = GeoJSON . Polygon | GeoJSON . MultiPolygon ;
11
- type CountrySelectionDatum =
12
- GeoJSON . Feature < GeoJSON . Geometry , CountryGeometry >
13
- | GeoJSON . FeatureCollection < GeoJSON . Geometry , CountryGeometry > ;
14
11
type WorldTopology = Topology < { land : GeometryCollection , countries : GeometryCollection < CountryGeometry > } > ;
15
12
16
13
export interface RenderOptions extends BaseRenderOptions {
17
14
topoJsonUrl : string ;
18
15
data$ : Observable < GeoDatum [ ] > ;
19
16
}
20
17
18
+ type AppendTerritoryPathFunction < L extends TerritoryLevel > =
19
+ ( datum : GeoDatum < L > , maxValue : number ) => d3 . Selection < SVGPathElement , any , null , undefined > | null ;
20
+
21
21
export class GeoMapD3 extends BaseD3 < RenderOptions > {
22
22
static minOpacity = .2 ;
23
23
static latitudeBounds = [ - 84 , 84 ] ;
@@ -33,7 +33,7 @@ export class GeoMapD3 extends BaseD3<RenderOptions> {
33
33
private landPath : d3 . Selection < SVGPathElement , GeoJSON . FeatureCollection < GeoJSON . Geometry , { } > , null , undefined > ;
34
34
private boundaryPath : d3 . Selection < SVGPathElement , GeoJSON . MultiLineString , null , undefined > ;
35
35
private dataG : d3 . Selection < SVGGElement , unknown , null , undefined > ;
36
- private dataPaths : d3 . Selection < SVGPathElement , CountrySelectionDatum , null , undefined > [ ] ;
36
+ private territoryPaths : d3 . Selection < SVGPathElement , any , null , undefined > [ ] ;
37
37
38
38
private static accessValue ( datum : GeoDatum ) {
39
39
return datum . values . activeUsers ;
@@ -181,7 +181,7 @@ export class GeoMapD3 extends BaseD3<RenderOptions> {
181
181
182
182
this . lastTransform = transform ;
183
183
184
- [ this . landPath , this . boundaryPath , ...this . dataPaths ]
184
+ [ this . landPath , this . boundaryPath , ...this . territoryPaths ]
185
185
. forEach ( dataPath => dataPath . attr ( 'd' , this . geoPath ) ) ;
186
186
}
187
187
@@ -190,29 +190,63 @@ export class GeoMapD3 extends BaseD3<RenderOptions> {
190
190
191
191
private renderData ( ) {
192
192
this . dataG = this . svg . insert ( 'g' , '.geo_map-boundary' ) ;
193
- this . dataPaths = [ ] ;
193
+ this . territoryPaths = [ ] ;
194
194
}
195
195
196
- private updateData ( data : GeoDatum [ ] ) {
196
+ private updateData < L extends TerritoryLevel > ( data : GeoDatum < L > [ ] ) {
197
197
this . dataG . html ( '' ) ;
198
198
199
- const { colorPrimary, minOpacity, accessValue } = GeoMapD3 ;
199
+ const { accessValue } = GeoMapD3 ;
200
+ const maxValue = data . reduce ( ( acc , datum ) => Math . max ( acc , accessValue ( datum ) ) , 0 ) ;
201
+ this . territoryPaths = data
202
+ . map ( datum => this . appendTerritoryPath ( datum , maxValue ) )
203
+ . filter ( ( dataPath ) : dataPath is d3 . Selection < SVGPathElement , any , null , undefined > => dataPath !== null ) ;
204
+ }
205
+
206
+ private appendTerritoryPath < L extends TerritoryLevel > ( datum : GeoDatum < L > , maxValue : number ) {
207
+ const appendFunction = {
208
+ [ TerritoryLevel . CONTINENT ] : this . appendContinentPath ,
209
+ [ TerritoryLevel . SUBCONTINENT ] : this . appendSubcontinentPath ,
210
+ [ TerritoryLevel . COUNTRY ] : this . appendCountryPath ,
211
+ [ TerritoryLevel . CITY ] : this . appendCityPath ,
212
+ } [ datum . territory . level ] as AppendTerritoryPathFunction < L > ;
213
+ return appendFunction . call ( this , datum , maxValue ) ;
214
+ }
215
+
216
+ private appendContinentPath : AppendTerritoryPathFunction < TerritoryLevel . CONTINENT > = ( datum , maxValue ) => {
217
+ return null ;
218
+ } ;
219
+
220
+ private appendSubcontinentPath : AppendTerritoryPathFunction < TerritoryLevel . SUBCONTINENT > = ( datum , maxValue ) => {
221
+ return null ;
222
+ } ;
200
223
224
+ private appendCountryPath : AppendTerritoryPathFunction < TerritoryLevel . COUNTRY > = ( datum , maxValue ) => {
225
+ const { accessValue } = GeoMapD3 ;
201
226
const { countries } = this . worldTopology . objects ;
202
- const maxValue = data . reduce ( ( acc , datum ) => Math . max ( acc , accessValue ( datum ) ) , 0 ) ;
227
+ const countryGeometryObject = countries . geometries . find ( geometry => geometry . id === datum . territory . id ) ;
228
+ if ( ! countryGeometryObject ) {
229
+ return null ;
230
+ }
231
+ const valueRatio = accessValue ( datum ) / maxValue ;
232
+ return this . dataG
233
+ . append ( 'path' )
234
+ . datum ( topojson . feature ( this . worldTopology , countryGeometryObject ) )
235
+ . attr ( 'd' , this . geoPath )
236
+ . call ( this . styleTerritory ( valueRatio ) ) ;
237
+ } ;
238
+
239
+ private appendCityPath : AppendTerritoryPathFunction < TerritoryLevel . CITY > = ( datum , maxValue ) => {
240
+ return null ;
241
+ } ;
242
+
243
+ private styleTerritory < T extends SVGGraphicsElement > ( valueRatio : number ) {
244
+ const { colorPrimary, minOpacity } = GeoMapD3 ;
203
245
204
- this . dataPaths = data . map ( datum => {
205
- const countryGeometryObject = countries . geometries . find ( geometry => geometry . id === datum . id ) ;
206
- if ( ! countryGeometryObject ) {
207
- return null ;
208
- }
209
- const valueRatio = accessValue ( datum ) / maxValue ;
210
- return this . dataG
211
- . append ( 'path' )
212
- . datum ( topojson . feature ( this . worldTopology , countryGeometryObject ) )
213
- . attr ( 'd' , this . geoPath )
246
+ return ( selection : d3 . Selection < SVGPathElement , any , null , undefined > ) => {
247
+ selection
214
248
. attr ( 'fill' , colorPrimary )
215
249
. attr ( 'opacity' , minOpacity + valueRatio * ( 1 - minOpacity ) ) ;
216
- } ) . filter ( ( dataPath ) : dataPath is d3 . Selection < SVGPathElement , CountrySelectionDatum , null , undefined > => dataPath !== null ) ;
250
+ } ;
217
251
}
218
252
}
0 commit comments