@flowmap.gl/data 8.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +199 -0
- package/dist/FlowMapAggregateAccessors.d.ts +15 -0
- package/dist/FlowMapAggregateAccessors.d.ts.map +1 -0
- package/dist/FlowMapAggregateAccessors.js +43 -0
- package/dist/FlowMapSelectors.d.ts +156 -0
- package/dist/FlowMapSelectors.d.ts.map +1 -0
- package/dist/FlowMapSelectors.js +831 -0
- package/dist/FlowMapState.d.ts +24 -0
- package/dist/FlowMapState.d.ts.map +1 -0
- package/dist/FlowMapState.js +2 -0
- package/dist/cluster/ClusterIndex.d.ts +42 -0
- package/dist/cluster/ClusterIndex.d.ts.map +1 -0
- package/dist/cluster/ClusterIndex.js +178 -0
- package/dist/cluster/cluster.d.ts +31 -0
- package/dist/cluster/cluster.d.ts.map +1 -0
- package/dist/cluster/cluster.js +206 -0
- package/dist/colors.d.ts +103 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +441 -0
- package/dist/getViewStateForLocations.d.ts +16 -0
- package/dist/getViewStateForLocations.d.ts.map +1 -0
- package/dist/getViewStateForLocations.js +30 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/provider/FlowMapDataProvider.d.ts +16 -0
- package/dist/provider/FlowMapDataProvider.d.ts.map +1 -0
- package/dist/provider/FlowMapDataProvider.js +17 -0
- package/dist/provider/LocalFlowMapDataProvider.d.ts +20 -0
- package/dist/provider/LocalFlowMapDataProvider.d.ts.map +1 -0
- package/dist/provider/LocalFlowMapDataProvider.js +87 -0
- package/dist/time.d.ts +24 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +126 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +4 -0
- package/package.json +48 -0
- package/src/FlowMapAggregateAccessors.ts +60 -0
- package/src/FlowMapSelectors.ts +1407 -0
- package/src/FlowMapState.ts +26 -0
- package/src/cluster/ClusterIndex.ts +266 -0
- package/src/cluster/cluster.ts +299 -0
- package/src/colors.ts +723 -0
- package/src/getViewStateForLocations.ts +64 -0
- package/src/index.ts +10 -0
- package/src/provider/FlowMapDataProvider.ts +63 -0
- package/src/provider/LocalFlowMapDataProvider.ts +108 -0
- package/src/time.ts +160 -0
- package/src/types.ts +162 -0
- package/src/util.ts +3 -0
- package/tsconfig.json +11 -0
- package/typings.d.ts +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {BoundingBox, viewport} from '@mapbox/geo-viewport';
|
|
2
|
+
import {geoBounds} from 'd3-geo';
|
|
3
|
+
import {FeatureCollection, GeometryCollection, GeometryObject} from 'geojson';
|
|
4
|
+
import {ViewState} from './types';
|
|
5
|
+
|
|
6
|
+
export type LocationProperties = any;
|
|
7
|
+
|
|
8
|
+
export function getViewStateForFeatures(
|
|
9
|
+
featureCollection:
|
|
10
|
+
| FeatureCollection<GeometryObject, LocationProperties>
|
|
11
|
+
| GeometryCollection,
|
|
12
|
+
size: [number, number],
|
|
13
|
+
opts?: {
|
|
14
|
+
pad?: number;
|
|
15
|
+
tileSize?: number;
|
|
16
|
+
minZoom?: number;
|
|
17
|
+
maxZoom?: number;
|
|
18
|
+
},
|
|
19
|
+
): ViewState {
|
|
20
|
+
const {pad = 0.05, tileSize = 512, minZoom = 0, maxZoom = 100} = opts || {};
|
|
21
|
+
const [[x1, y1], [x2, y2]] = geoBounds(featureCollection as any);
|
|
22
|
+
const bounds: BoundingBox = [
|
|
23
|
+
x1 - pad * (x2 - x1),
|
|
24
|
+
y1 - pad * (y2 - y1),
|
|
25
|
+
x2 + pad * (x2 - x1),
|
|
26
|
+
y2 + pad * (y2 - y1),
|
|
27
|
+
];
|
|
28
|
+
const {
|
|
29
|
+
center: [longitude, latitude],
|
|
30
|
+
zoom,
|
|
31
|
+
} = viewport(bounds, size, undefined, undefined, tileSize, true);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
longitude,
|
|
35
|
+
latitude,
|
|
36
|
+
zoom: Math.max(Math.min(maxZoom, zoom), minZoom),
|
|
37
|
+
bearing: 0,
|
|
38
|
+
pitch: 0,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getViewStateForLocations(
|
|
43
|
+
locations: any[],
|
|
44
|
+
getLocationCentroid: (location: any) => [number, number],
|
|
45
|
+
size: [number, number],
|
|
46
|
+
opts?: {
|
|
47
|
+
pad?: number;
|
|
48
|
+
tileSize?: number;
|
|
49
|
+
minZoom?: number;
|
|
50
|
+
maxZoom?: number;
|
|
51
|
+
},
|
|
52
|
+
): ViewState {
|
|
53
|
+
return getViewStateForFeatures(
|
|
54
|
+
{
|
|
55
|
+
type: 'GeometryCollection',
|
|
56
|
+
geometries: locations.map((location) => ({
|
|
57
|
+
type: 'Point',
|
|
58
|
+
coordinates: getLocationCentroid(location),
|
|
59
|
+
})),
|
|
60
|
+
} as any,
|
|
61
|
+
size,
|
|
62
|
+
opts,
|
|
63
|
+
);
|
|
64
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './colors';
|
|
3
|
+
export * from './FlowMapState';
|
|
4
|
+
export * from './FlowMapSelectors';
|
|
5
|
+
export * from './time';
|
|
6
|
+
export * from './getViewStateForLocations';
|
|
7
|
+
export * from './provider/FlowMapDataProvider';
|
|
8
|
+
export {default as FlowMapAggregateAccessors} from './FlowMapAggregateAccessors';
|
|
9
|
+
export type {default as FlowMapDataProvider} from './provider/FlowMapDataProvider';
|
|
10
|
+
export {default as LocalFlowMapDataProvider} from './provider/LocalFlowMapDataProvider';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {AggregateFlow, Cluster, LocationAccessors, LocationTotals} from '..';
|
|
2
|
+
import {FlowMapState} from '../FlowMapState';
|
|
3
|
+
import {
|
|
4
|
+
ClusterNode,
|
|
5
|
+
FlowMapData,
|
|
6
|
+
FlowMapDataAccessors,
|
|
7
|
+
LayersData,
|
|
8
|
+
ViewportProps,
|
|
9
|
+
} from '../types';
|
|
10
|
+
|
|
11
|
+
export default interface FlowMapDataProvider<L, F> {
|
|
12
|
+
setAccessors(accessors: FlowMapDataAccessors<L, F>): void;
|
|
13
|
+
|
|
14
|
+
setFlowMapState(flowMapState: FlowMapState): Promise<void>;
|
|
15
|
+
|
|
16
|
+
// clearData(): void;
|
|
17
|
+
|
|
18
|
+
getViewportForLocations(dims: [number, number]): Promise<ViewportProps>;
|
|
19
|
+
|
|
20
|
+
// getFlowTotals(): Promise<FlowTotals>;
|
|
21
|
+
|
|
22
|
+
getFlowByIndex(index: number): Promise<F | AggregateFlow | undefined>;
|
|
23
|
+
|
|
24
|
+
getLocationById(id: string): Promise<L | Cluster | undefined>;
|
|
25
|
+
|
|
26
|
+
getLocationByIndex(idx: number): Promise<L | ClusterNode | undefined>;
|
|
27
|
+
|
|
28
|
+
getTotalsForLocation(id: string): Promise<LocationTotals | undefined>;
|
|
29
|
+
|
|
30
|
+
// getLocationsInBbox(
|
|
31
|
+
// bbox: [number, number, number, number],
|
|
32
|
+
// ): Promise<Array<FlowLocation | ClusterNode> | undefined>;
|
|
33
|
+
|
|
34
|
+
// getLocationsForSearchBox(): Promise<(FlowLocation | ClusterNode)[] | undefined>;
|
|
35
|
+
|
|
36
|
+
getLayersData(): Promise<LayersData | undefined>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isFlowMapData<L, F>(
|
|
40
|
+
data: Record<string, any>,
|
|
41
|
+
): data is FlowMapData<L, F> {
|
|
42
|
+
return (
|
|
43
|
+
data &&
|
|
44
|
+
data.locations &&
|
|
45
|
+
data.flows &&
|
|
46
|
+
Array.isArray(data.locations) &&
|
|
47
|
+
Array.isArray(data.flows)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isFlowMapDataProvider<L, F>(
|
|
52
|
+
dataProvider: Record<string, any>,
|
|
53
|
+
): dataProvider is FlowMapDataProvider<L, F> {
|
|
54
|
+
return (
|
|
55
|
+
dataProvider &&
|
|
56
|
+
typeof dataProvider.setFlowMapState === 'function' &&
|
|
57
|
+
typeof dataProvider.getViewportForLocations === 'function' &&
|
|
58
|
+
typeof dataProvider.getFlowByIndex === 'function' &&
|
|
59
|
+
typeof dataProvider.getLocationById === 'function' &&
|
|
60
|
+
typeof dataProvider.getLocationByIndex === 'function' &&
|
|
61
|
+
typeof dataProvider.getLayersData === 'function'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import FlowMapDataProvider from './FlowMapDataProvider';
|
|
2
|
+
import {
|
|
3
|
+
Cluster,
|
|
4
|
+
ClusterNode,
|
|
5
|
+
FlowAccessors,
|
|
6
|
+
FlowMapData,
|
|
7
|
+
FlowMapDataAccessors,
|
|
8
|
+
LayersData,
|
|
9
|
+
LocationAccessors,
|
|
10
|
+
LocationTotals,
|
|
11
|
+
ViewportProps,
|
|
12
|
+
} from '../types';
|
|
13
|
+
import {FlowMapState} from '../FlowMapState';
|
|
14
|
+
import FlowMapSelectors from '../FlowMapSelectors';
|
|
15
|
+
import {AggregateFlow} from '..';
|
|
16
|
+
|
|
17
|
+
export default class LocalFlowMapDataProvider<L, F>
|
|
18
|
+
implements FlowMapDataProvider<L, F>
|
|
19
|
+
{
|
|
20
|
+
private selectors: FlowMapSelectors<L, F>;
|
|
21
|
+
private flowMapData: FlowMapData<L, F> | undefined;
|
|
22
|
+
private flowMapState: FlowMapState | undefined;
|
|
23
|
+
|
|
24
|
+
constructor(accessors: FlowMapDataAccessors<L, F>) {
|
|
25
|
+
// scope selectors to the concrete instance of FlowMapDataProvider
|
|
26
|
+
this.selectors = new FlowMapSelectors<L, F>(accessors);
|
|
27
|
+
this.flowMapData = undefined;
|
|
28
|
+
this.flowMapState = undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setAccessors(accessors: FlowMapDataAccessors<L, F>) {
|
|
32
|
+
this.selectors.setAccessors(accessors);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async setFlowMapData(flowMapData: FlowMapData<L, F>): Promise<void> {
|
|
36
|
+
this.flowMapData = flowMapData;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async setFlowMapState(flowMapState: FlowMapState): Promise<void> {
|
|
40
|
+
this.flowMapState = flowMapState;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async getFlowByIndex(idx: number): Promise<F | AggregateFlow | undefined> {
|
|
44
|
+
if (!this.flowMapState || !this.flowMapData) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const flows = this.selectors.getFlowsForFlowMapLayer(
|
|
48
|
+
this.flowMapState,
|
|
49
|
+
this.flowMapData,
|
|
50
|
+
);
|
|
51
|
+
return flows?.[idx];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getLocationByIndex(idx: number): Promise<L | ClusterNode | undefined> {
|
|
55
|
+
if (!this.flowMapState || !this.flowMapData) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const locations = this.selectors.getLocationsForFlowMapLayer(
|
|
59
|
+
this.flowMapState,
|
|
60
|
+
this.flowMapData,
|
|
61
|
+
);
|
|
62
|
+
return locations?.[idx];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getLayersData(): Promise<LayersData | undefined> {
|
|
66
|
+
if (!this.flowMapState || !this.flowMapData) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return this.selectors.prepareLayersData(
|
|
70
|
+
this.flowMapState,
|
|
71
|
+
this.flowMapData,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async getLocationById(id: string): Promise<L | Cluster | undefined> {
|
|
76
|
+
if (!this.flowMapState || !this.flowMapData) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const clusterIndex = this.selectors.getClusterIndex(
|
|
80
|
+
this.flowMapState,
|
|
81
|
+
this.flowMapData,
|
|
82
|
+
);
|
|
83
|
+
if (clusterIndex) {
|
|
84
|
+
const cluster = clusterIndex.getClusterById(id);
|
|
85
|
+
if (cluster) {
|
|
86
|
+
return cluster;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const locationsById = this.selectors.getLocationsById(
|
|
90
|
+
this.flowMapState,
|
|
91
|
+
this.flowMapData,
|
|
92
|
+
);
|
|
93
|
+
return locationsById?.get(id);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getTotalsForLocation(id: string): Promise<LocationTotals | undefined> {
|
|
97
|
+
if (!this.flowMapState || !this.flowMapData) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return this.selectors
|
|
101
|
+
.getLocationTotals(this.flowMapState, this.flowMapData)
|
|
102
|
+
?.get(id);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getViewportForLocations(dims: [number, number]): Promise<ViewportProps> {
|
|
106
|
+
return Promise.resolve({} as ViewportProps);
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/time.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {timeFormat, timeParse} from 'd3-time-format';
|
|
2
|
+
import {
|
|
3
|
+
timeDay,
|
|
4
|
+
timeHour,
|
|
5
|
+
TimeInterval,
|
|
6
|
+
timeMinute,
|
|
7
|
+
timeMonth,
|
|
8
|
+
timeSecond,
|
|
9
|
+
timeWeek,
|
|
10
|
+
timeYear,
|
|
11
|
+
} from 'd3-time';
|
|
12
|
+
|
|
13
|
+
const dateParsers = [
|
|
14
|
+
timeParse('%Y-%m-%d'),
|
|
15
|
+
timeParse('%Y-%m-%d %H:%M'),
|
|
16
|
+
timeParse('%Y-%m-%d %H:%M:%S'),
|
|
17
|
+
timeParse('%Y'),
|
|
18
|
+
timeParse('%Y-%m'),
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function parseTime(input: string | Date | undefined): Date | undefined {
|
|
22
|
+
if (input != null) {
|
|
23
|
+
if (input instanceof Date) {
|
|
24
|
+
return input;
|
|
25
|
+
}
|
|
26
|
+
for (const parse of dateParsers) {
|
|
27
|
+
const date = parse(input);
|
|
28
|
+
if (date) {
|
|
29
|
+
return date;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export enum TimeGranularityKey {
|
|
37
|
+
SECOND = 'SECOND',
|
|
38
|
+
MINUTE = 'MINUTE',
|
|
39
|
+
HOUR = 'HOUR',
|
|
40
|
+
DAY = 'DAY',
|
|
41
|
+
MONTH = 'MONTH',
|
|
42
|
+
YEAR = 'YEAR',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface TimeGranularity {
|
|
46
|
+
key: TimeGranularityKey;
|
|
47
|
+
order: number;
|
|
48
|
+
interval: TimeInterval;
|
|
49
|
+
format: (date: Date) => string;
|
|
50
|
+
formatFull: (date: Date) => string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// const preferredLocale = navigator.languages ? navigator.languages[0] : 'en';
|
|
54
|
+
|
|
55
|
+
const formatMillisecond = timeFormat('.%L'),
|
|
56
|
+
formatSecond = timeFormat(':%S'),
|
|
57
|
+
formatMinute = timeFormat('%I:%M'),
|
|
58
|
+
// formatHour = (d: Date) => d.toLocaleString(preferredLocale, { hour: 'numeric' }),
|
|
59
|
+
formatHour = timeFormat('%I %p'),
|
|
60
|
+
formatDay = timeFormat('%a %d'),
|
|
61
|
+
formatWeek = timeFormat('%b %d'),
|
|
62
|
+
formatMonth = timeFormat('%b'),
|
|
63
|
+
formatYear = timeFormat('%Y');
|
|
64
|
+
|
|
65
|
+
export function tickMultiFormat(date: Date) {
|
|
66
|
+
return (
|
|
67
|
+
timeSecond(date) < date
|
|
68
|
+
? formatMillisecond
|
|
69
|
+
: timeMinute(date) < date
|
|
70
|
+
? formatSecond
|
|
71
|
+
: timeHour(date) < date
|
|
72
|
+
? formatMinute
|
|
73
|
+
: timeDay(date) < date
|
|
74
|
+
? formatHour
|
|
75
|
+
: timeMonth(date) < date
|
|
76
|
+
? timeWeek(date) < date
|
|
77
|
+
? formatDay
|
|
78
|
+
: formatWeek
|
|
79
|
+
: timeYear(date) < date
|
|
80
|
+
? formatMonth
|
|
81
|
+
: formatYear
|
|
82
|
+
)(date);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const TIME_GRANULARITIES: TimeGranularity[] = [
|
|
86
|
+
{
|
|
87
|
+
order: 0,
|
|
88
|
+
key: TimeGranularityKey.SECOND,
|
|
89
|
+
interval: timeSecond,
|
|
90
|
+
format: formatSecond,
|
|
91
|
+
formatFull: timeFormat('%Y-%m-%d %H:%M:%S'),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
order: 1,
|
|
95
|
+
key: TimeGranularityKey.MINUTE,
|
|
96
|
+
interval: timeMinute,
|
|
97
|
+
format: formatMinute,
|
|
98
|
+
formatFull: timeFormat('%Y-%m-%d %H:%M'),
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
order: 2,
|
|
102
|
+
key: TimeGranularityKey.HOUR,
|
|
103
|
+
interval: timeHour,
|
|
104
|
+
// format: (d: Date) => d.toLocaleString(preferredLocale, { hour: 'numeric', minute: '2-digit' }),
|
|
105
|
+
format: formatHour,
|
|
106
|
+
formatFull: timeFormat('%a %d %b %Y, %I %p'),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
order: 3,
|
|
110
|
+
key: TimeGranularityKey.DAY,
|
|
111
|
+
interval: timeDay,
|
|
112
|
+
format: formatDay,
|
|
113
|
+
formatFull: timeFormat('%a %d %b %Y'),
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
order: 4,
|
|
117
|
+
key: TimeGranularityKey.MONTH,
|
|
118
|
+
interval: timeMonth,
|
|
119
|
+
format: formatMonth,
|
|
120
|
+
formatFull: timeFormat('%b %Y'),
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
order: 5,
|
|
124
|
+
key: TimeGranularityKey.YEAR,
|
|
125
|
+
interval: timeYear,
|
|
126
|
+
format: formatYear,
|
|
127
|
+
formatFull: timeFormat('%Y'),
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
export function getTimeGranularityByKey(key: TimeGranularityKey) {
|
|
132
|
+
return TIME_GRANULARITIES.find((s) => s.key === key);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function getTimeGranularityByOrder(order: number) {
|
|
136
|
+
return TIME_GRANULARITIES.find((s) => s.order === order);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function getTimeGranularityForDate(date: Date): TimeGranularity {
|
|
140
|
+
let prev = undefined;
|
|
141
|
+
for (const current of TIME_GRANULARITIES) {
|
|
142
|
+
const {interval} = current;
|
|
143
|
+
const floored = interval(date);
|
|
144
|
+
if (floored < date) {
|
|
145
|
+
if (!prev) return current;
|
|
146
|
+
return prev;
|
|
147
|
+
}
|
|
148
|
+
prev = current;
|
|
149
|
+
}
|
|
150
|
+
return TIME_GRANULARITIES[TIME_GRANULARITIES.length - 1];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function areRangesEqual(
|
|
154
|
+
a: [Date, Date] | undefined,
|
|
155
|
+
b: [Date, Date] | undefined,
|
|
156
|
+
): boolean {
|
|
157
|
+
if (!a && !b) return true;
|
|
158
|
+
if (!a || !b) return false;
|
|
159
|
+
return a[0] === b[0] && a[1] === b[1];
|
|
160
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export type FlowMapData<L, F> = {
|
|
2
|
+
locations: L[] | undefined;
|
|
3
|
+
flows: F[] | undefined;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export interface ViewState {
|
|
7
|
+
latitude: number;
|
|
8
|
+
longitude: number;
|
|
9
|
+
zoom: number;
|
|
10
|
+
bearing?: number;
|
|
11
|
+
pitch?: number;
|
|
12
|
+
altitude?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type FlowAccessor<F, T> = (flow: F) => T; // objectInfo?: AccessorObjectInfo,
|
|
16
|
+
export type LocationAccessor<L, T> = (location: L) => T;
|
|
17
|
+
|
|
18
|
+
export interface FlowAccessors<F> {
|
|
19
|
+
getFlowOriginId: FlowAccessor<F, string>;
|
|
20
|
+
getFlowDestId: FlowAccessor<F, string>;
|
|
21
|
+
getFlowMagnitude: FlowAccessor<F, number>;
|
|
22
|
+
getFlowTime?: FlowAccessor<F, Date>; // TODO: use number instead of Date
|
|
23
|
+
// getFlowColor?: FlowAccessor<string | undefined>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LocationAccessors<L> {
|
|
27
|
+
getLocationId: LocationAccessor<L, string>;
|
|
28
|
+
getLocationName?: LocationAccessor<L, string>;
|
|
29
|
+
getLocationCentroid: LocationAccessor<L, [number, number]>;
|
|
30
|
+
getLocationClusterName?: (locationIds: string[]) => string;
|
|
31
|
+
// getLocationTotalIn?: LocationAccessor<number>;
|
|
32
|
+
// getLocationTotalOut?: LocationAccessor<number>;
|
|
33
|
+
// getLocationTotalInternal?: LocationAccessor<number>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type FlowMapDataAccessors<L, F> = LocationAccessors<L> &
|
|
37
|
+
FlowAccessors<F>;
|
|
38
|
+
|
|
39
|
+
export interface LocationTotals {
|
|
40
|
+
incomingCount: number;
|
|
41
|
+
outgoingCount: number;
|
|
42
|
+
internalCount: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// export interface LocationsTotals {
|
|
46
|
+
// incoming: {[id: string]: number};
|
|
47
|
+
// outgoing: {[id: string]: number};
|
|
48
|
+
// internal: {[id: string]: number};
|
|
49
|
+
// }
|
|
50
|
+
|
|
51
|
+
export interface CountByTime {
|
|
52
|
+
time: Date;
|
|
53
|
+
count: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ViewportProps {
|
|
57
|
+
width: number;
|
|
58
|
+
height: number;
|
|
59
|
+
latitude: number;
|
|
60
|
+
longitude: number;
|
|
61
|
+
zoom: number;
|
|
62
|
+
bearing: number;
|
|
63
|
+
pitch: number;
|
|
64
|
+
altitude?: number;
|
|
65
|
+
maxZoom?: number;
|
|
66
|
+
minZoom?: number;
|
|
67
|
+
maxPitch?: number;
|
|
68
|
+
minPitch?: number;
|
|
69
|
+
transitionDuration?: number | 'auto';
|
|
70
|
+
transitionInterpolator?: any;
|
|
71
|
+
transitionInterruption?: any;
|
|
72
|
+
transitionEasing?: any;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ClusterNode {
|
|
76
|
+
id: string;
|
|
77
|
+
zoom: number;
|
|
78
|
+
centroid: [number, number];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ClusterLevel {
|
|
82
|
+
zoom: number;
|
|
83
|
+
nodes: ClusterNode[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type ClusterLevels = ClusterLevel[];
|
|
87
|
+
|
|
88
|
+
// non-leaf cluster node
|
|
89
|
+
export interface Cluster extends ClusterNode {
|
|
90
|
+
name?: string;
|
|
91
|
+
children: string[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function isCluster(c: ClusterNode): c is Cluster {
|
|
95
|
+
const {children} = c as Cluster;
|
|
96
|
+
return children && children.length > 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function isLocationClusterNode<L>(l: L | ClusterNode): l is ClusterNode {
|
|
100
|
+
const {zoom} = l as ClusterNode;
|
|
101
|
+
return zoom !== undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface AggregateFlow {
|
|
105
|
+
origin: string;
|
|
106
|
+
dest: string;
|
|
107
|
+
count: number;
|
|
108
|
+
aggregate: true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function isAggregateFlow(
|
|
112
|
+
flow: Record<string, any>,
|
|
113
|
+
): flow is AggregateFlow {
|
|
114
|
+
return (
|
|
115
|
+
flow &&
|
|
116
|
+
flow.origin !== undefined &&
|
|
117
|
+
flow.dest !== undefined &&
|
|
118
|
+
flow.count !== undefined &&
|
|
119
|
+
(flow.aggregate ? true : false)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface FlowCountsMapReduce<F, T = any> {
|
|
124
|
+
map: (flow: F) => T;
|
|
125
|
+
reduce: (accumulated: T, val: T) => T;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export enum LocationFilterMode {
|
|
129
|
+
ALL = 'ALL',
|
|
130
|
+
INCOMING = 'INCOMING',
|
|
131
|
+
OUTGOING = 'OUTGOING',
|
|
132
|
+
BETWEEN = 'BETWEEN',
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface FlowCirclesLayerAttributes {
|
|
136
|
+
length: number;
|
|
137
|
+
attributes: {
|
|
138
|
+
getPosition: LayersDataAttrValues<Float32Array>;
|
|
139
|
+
getColor: LayersDataAttrValues<Uint8Array>;
|
|
140
|
+
getInRadius: LayersDataAttrValues<Float32Array>;
|
|
141
|
+
getOutRadius: LayersDataAttrValues<Float32Array>;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface FlowLinesLayerAttributes {
|
|
146
|
+
length: number;
|
|
147
|
+
attributes: {
|
|
148
|
+
getSourcePosition: LayersDataAttrValues<Float32Array>;
|
|
149
|
+
getTargetPosition: LayersDataAttrValues<Float32Array>;
|
|
150
|
+
getThickness: LayersDataAttrValues<Float32Array>;
|
|
151
|
+
getColor: LayersDataAttrValues<Uint8Array>;
|
|
152
|
+
getEndpointOffsets: LayersDataAttrValues<Float32Array>;
|
|
153
|
+
getStaggering?: LayersDataAttrValues<Float32Array>;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface LayersData {
|
|
158
|
+
circleAttributes: FlowCirclesLayerAttributes;
|
|
159
|
+
lineAttributes: FlowLinesLayerAttributes;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export type LayersDataAttrValues<T> = {value: T; size: number};
|
package/src/util.ts
ADDED
package/tsconfig.json
ADDED
package/typings.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module 'kdbush';
|