@dra2020/dra-types 1.8.87 → 1.8.89
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/dist/all.d.ts +6 -14
- package/dist/alldt.d.ts +14 -0
- package/dist/colormgr.d.ts +69 -0
- package/dist/dra-types.d.ts +1 -0
- package/dist/dra-types.js +801 -13
- package/dist/dra-types.js.map +1 -1
- package/dist/packedfields.d.ts +103 -1
- package/lib/all.ts +6 -14
- package/lib/alldt.ts +14 -0
- package/lib/colormgr.ts +563 -0
- package/lib/dra-types.ts +1 -0
- package/lib/packedfields.ts +396 -1
- package/package.json +2 -2
package/lib/colormgr.ts
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
// App libraries
|
|
2
|
+
import * as PF from "./packedfields";
|
|
3
|
+
import {Util, Colors} from '@dra2020/baseclient'
|
|
4
|
+
|
|
5
|
+
// All Groups Mosaic has 16 colors
|
|
6
|
+
// Index values into the 16 colors
|
|
7
|
+
export const ColorBySolidWhite = 0;
|
|
8
|
+
export const ColorBySolidBlack = 1;
|
|
9
|
+
export const ColorBySolidHispanic = 2;
|
|
10
|
+
export const ColorBySolidAsian = 3;
|
|
11
|
+
export const ColorByMostlyWhite = 4;
|
|
12
|
+
export const ColorByMostlyBlack = 5;
|
|
13
|
+
export const ColorByMostlyHispanic = 6;
|
|
14
|
+
export const ColorByMostlyAsian = 7;
|
|
15
|
+
export const ColorByMostlyNative = 8;
|
|
16
|
+
export const ColorByMix = 9;
|
|
17
|
+
export const ColorByHispanicWhite = 10;
|
|
18
|
+
export const ColorByBlackWhite = 11;
|
|
19
|
+
export const ColorByHispanicBlack = 12;
|
|
20
|
+
export const ColorByAsianWhite = 13;
|
|
21
|
+
export const ColorByAsianHispanic = 14;
|
|
22
|
+
export const ColorByBlackAsian = 15;
|
|
23
|
+
|
|
24
|
+
// Text color for All Groups Mosaic (Legend)
|
|
25
|
+
export const EthnicTextColor: string[] = [
|
|
26
|
+
'#ffffff',
|
|
27
|
+
'#ffffff',
|
|
28
|
+
'#ffffff',
|
|
29
|
+
'#ffffff',
|
|
30
|
+
'#000000',
|
|
31
|
+
'#000000',
|
|
32
|
+
'#000000',
|
|
33
|
+
'#000000',
|
|
34
|
+
'#000000',
|
|
35
|
+
'#000000',
|
|
36
|
+
'#000000',
|
|
37
|
+
'#000000',
|
|
38
|
+
'#ffffff',
|
|
39
|
+
'#000000',
|
|
40
|
+
'#000000',
|
|
41
|
+
'#ffffff',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export const ColorByGreatR = 0;
|
|
45
|
+
export const ColorByGoodR = 1;
|
|
46
|
+
export const ColorByFairR = 2;
|
|
47
|
+
export const ColorByEven = 3;
|
|
48
|
+
export const ColorByFairD = 4;
|
|
49
|
+
export const ColorByGoodD = 5;
|
|
50
|
+
export const ColorByGreatD = 6;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
// ****************** Colors, Color Stops and Color Functions for "classic" palettes *******************
|
|
54
|
+
const PartisanFewStops = [
|
|
55
|
+
0.00, // Carmine
|
|
56
|
+
0.20, //
|
|
57
|
+
0.30, //
|
|
58
|
+
0.40, //
|
|
59
|
+
0.45, //
|
|
60
|
+
0.50, // pale red
|
|
61
|
+
0.50, // pale blue
|
|
62
|
+
0.55, //
|
|
63
|
+
0.60, //
|
|
64
|
+
0.70, //
|
|
65
|
+
0.80, //
|
|
66
|
+
1.00, // Dark blue
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Like PartisanFewStops, but lose the gradient at two buckets at each edge to focus on competitive district differentiation
|
|
70
|
+
export let PartisanDistrictStops = [
|
|
71
|
+
// Dark red
|
|
72
|
+
//{ stop: 0.00, color: '#8B0000' }, // Dark red
|
|
73
|
+
//{ stop: 0.40, color: '#8B0000' }, // .00 <= .40
|
|
74
|
+
|
|
75
|
+
// Crimson
|
|
76
|
+
//{ stop: 0.00, color: '#DC143C' }, // Crimson
|
|
77
|
+
//{ stop: 0.40, color: '#DC143C' }, // .00 <= .40
|
|
78
|
+
|
|
79
|
+
// Chilli Red
|
|
80
|
+
//{ stop: 0.00, color: '#C21807' }, // Chilli Red
|
|
81
|
+
//{ stop: 0.40, color: '#C21807' }, // .00 <= .40
|
|
82
|
+
|
|
83
|
+
// Carmine
|
|
84
|
+
0.00, // Carmine
|
|
85
|
+
0.40, // .00 <= .40
|
|
86
|
+
|
|
87
|
+
0.40, //
|
|
88
|
+
0.45, // .40 <= .45
|
|
89
|
+
0.45, //
|
|
90
|
+
0.50, // .45 <= .50
|
|
91
|
+
0.50, //
|
|
92
|
+
0.55, // .50 <= .55
|
|
93
|
+
0.55, //
|
|
94
|
+
0.60, // .55 <= .60
|
|
95
|
+
0.60, //
|
|
96
|
+
1.00, // .60 <= 1.0
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
/*export let PartisanCustomStops = [
|
|
100
|
+
{ stop: 0.00, color: '#FF0000' }, // Dark red
|
|
101
|
+
{ stop: 0.20, color: '#FF0000' }, //
|
|
102
|
+
{ stop: 0.30, color: '#FF0000' }, //
|
|
103
|
+
{ stop: 0.40, color: '#CD5C5C' }, // Indian red
|
|
104
|
+
{ stop: 0.45, color: '#DB7093' }, // Pale Violet Red
|
|
105
|
+
{ stop: 0.499, color: '#DB7093' }, // Light Pink
|
|
106
|
+
{ stop: 0.50, color: '#FFFFFF' }, // white
|
|
107
|
+
{ stop: 0.501, color: '#87CEFA' }, // Light Sky Blue
|
|
108
|
+
{ stop: 0.55, color: '#00bFFF' }, // Deep Sky Blue
|
|
109
|
+
{ stop: 0.60, color: '#002366' }, // Royal Blue
|
|
110
|
+
{ stop: 0.70, color: '#0000CD' }, // Medium Blue
|
|
111
|
+
{ stop: 0.80, color: '#191970' }, //
|
|
112
|
+
{ stop: 1.00, color: '#191970' }, // Midnight Blue
|
|
113
|
+
];*/
|
|
114
|
+
|
|
115
|
+
export let PartisanPrecinctStops = PartisanFewStops;
|
|
116
|
+
|
|
117
|
+
export let EthnicFewStops = [
|
|
118
|
+
0.00, //
|
|
119
|
+
0.40, //
|
|
120
|
+
0.50, //
|
|
121
|
+
1.00, //
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
export type PaletteDefaults = { [key: string]: string };
|
|
125
|
+
export const DefaultPaletteDefaults: PaletteDefaults = {
|
|
126
|
+
partisanScale: 'partisanclassic',
|
|
127
|
+
partisanDistrictsScale: 'partisandistrictsclassic',
|
|
128
|
+
demographicsScale: 'demographicsclassic',
|
|
129
|
+
demographicsAllGroups: 'allgroupsclassic',
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export function makeStops(stops: number[], colors: string[]): Util.Stop[]
|
|
133
|
+
{
|
|
134
|
+
// Lengths should be equal
|
|
135
|
+
const len: number = stops.length <= colors.length ? stops.length : colors.length;
|
|
136
|
+
let result: Util.Stop[] = []
|
|
137
|
+
for (let i = 0; i < len; i++)
|
|
138
|
+
result.push({stop: stops[i], color: colors[i]});
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function partisanStops(stops: number[], pd: PaletteDefaults): Util.Stop[]
|
|
143
|
+
{
|
|
144
|
+
const palette: PaletteName = pd['partisanScale'] as PaletteName;
|
|
145
|
+
return makeStops(stops, colorsFromStopsPartisan(palette, 'partisanScale', stops));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function partisanDistrictStops(stops: number[], pd: PaletteDefaults): Util.Stop[]
|
|
149
|
+
{
|
|
150
|
+
const palette: PaletteName = pd['partisanDistrictsScale'] as PaletteName;
|
|
151
|
+
return makeStops(stops, colorsFromStopsPartisan(palette, 'partisanDistrictsScale', stops));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function ethnicStops(stops: number[], pd: PaletteDefaults): Util.Stop[]
|
|
155
|
+
{
|
|
156
|
+
const palette: PaletteName = pd['demographicsScale'] as PaletteName;
|
|
157
|
+
if (palette === 'demographicsclassic')
|
|
158
|
+
return makeStops(stops, Colors.EthnicFewClassicColors);
|
|
159
|
+
return makeStops(stops, colorsFromStops(palette, stops, Colors.EthnicFewClassicColors));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function ethnicBackgroundColor(index: number, pd: PaletteDefaults): string
|
|
163
|
+
{
|
|
164
|
+
const palette: PaletteName = pd['demographicsAllGroups'] as PaletteName;
|
|
165
|
+
const colors: string[] = allGroups16Colors(palette, Colors.getPalette(palette));
|
|
166
|
+
if (index < colors.length)
|
|
167
|
+
return colors[index];
|
|
168
|
+
return '#ffffff';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function ToAllEthnicColor(agg: PF.PackedFields, dc: PF.DatasetContext, pd: PaletteDefaults): number
|
|
172
|
+
{
|
|
173
|
+
// Use VAP/CVAP if it exists
|
|
174
|
+
const dataset: string = dc.primeVDS ? dc.primeVDS : dc.primeDDS
|
|
175
|
+
return AggregateEthnicColor(PF.ToGetter(agg, dc, dataset), pd, dataset.endsWith('NH'));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function ToPartisanColorStr(agg: PF.PackedFields, dc: PF.DatasetContext, pd: PaletteDefaults): string
|
|
179
|
+
{
|
|
180
|
+
return ToPartisanColor(agg, dc, partisanStops(PartisanPrecinctStops, pd));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function ToPartisanDistrictColor(agg: PF.PackedFields, dc: PF.DatasetContext, pd: PaletteDefaults): string
|
|
184
|
+
{
|
|
185
|
+
return ToPartisanColor(agg, dc, partisanDistrictStops(PartisanDistrictStops, pd));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function ToPartisanColor(agg: PF.PackedFields, dc: PF.DatasetContext, stops: Util.GradientStops): string
|
|
189
|
+
{
|
|
190
|
+
if (dc.primeEDS === PF.DS_PVI2020)
|
|
191
|
+
{
|
|
192
|
+
const getter16 = PF.ToGetter(agg, dc, PF.DS_PRES2016);
|
|
193
|
+
const getter20 = PF.ToGetter(agg, dc, PF.DS_PRES2020);
|
|
194
|
+
|
|
195
|
+
const pviRaw = PF.calcRaw2020Pvi(getter16, getter20);
|
|
196
|
+
const color: string = ColorFromRGBPcts((1 - pviRaw / 100), 0, pviRaw / 100, stops);
|
|
197
|
+
//console.log('Pvi (r, d, color): (' + (1 - pviRaw/100) + ', ' + pviRaw/100 + ', ' + color + ')');
|
|
198
|
+
return color;
|
|
199
|
+
}
|
|
200
|
+
else if (dc.primeEDS === PF.DS_PVI2016)
|
|
201
|
+
{
|
|
202
|
+
const getter = PF.ToGetter(agg, dc, dc.primeEDS);
|
|
203
|
+
const pviRaw = PF.calcRawPvi(getter);
|
|
204
|
+
const color: string = ColorFromRGBPcts((1 - pviRaw/100), 0, pviRaw/100, stops);
|
|
205
|
+
return color;
|
|
206
|
+
}
|
|
207
|
+
else
|
|
208
|
+
{
|
|
209
|
+
const getter = PF.ToGetter(agg, dc, dc.primeEDS);
|
|
210
|
+
return AggregatePartisanColorStr(getter, stops);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function ToPartisanShiftColor(agg: PF.PackedFields, dc: PF.DatasetContext, datasets: string[], pd: PaletteDefaults): string
|
|
215
|
+
{
|
|
216
|
+
if (!datasets || datasets.length < 2)
|
|
217
|
+
return '';
|
|
218
|
+
|
|
219
|
+
const shift: number = PF.calcShift(agg, dc, datasets[0], datasets[1]);
|
|
220
|
+
if (shift == null)
|
|
221
|
+
return null;
|
|
222
|
+
const rep: number = 0.5 - (shift / 2);
|
|
223
|
+
const dem: number = 0.5 + (shift / 2);
|
|
224
|
+
const stops: Util.GradientStops = partisanStops(PartisanPrecinctStops, pd);
|
|
225
|
+
const color: string = ColorFromRGBPcts(rep, 0, dem, stops);
|
|
226
|
+
//console.log('Shift (r, d, color): (' + rep + ', ' + dem + ', ' + color + ')');
|
|
227
|
+
return color;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function ToEthnicColorStr(agg: PF.PackedFields, dc: PF.DatasetContext, pd: PaletteDefaults, detail: string): string
|
|
231
|
+
{
|
|
232
|
+
let ethnic: string = 'Wh';
|
|
233
|
+
let total: string = 'Tot';
|
|
234
|
+
let bInvert: boolean = false;
|
|
235
|
+
const dataset = dc.primeVDS ? dc.primeVDS : dc.primeDDS;
|
|
236
|
+
switch (detail)
|
|
237
|
+
{
|
|
238
|
+
case null: case '': case 'all':
|
|
239
|
+
let c = ToAllEthnicColor(agg, dc, pd); // special case
|
|
240
|
+
return c >= 0 ? ethnicBackgroundColor(c, pd) : '#ffffff';
|
|
241
|
+
case 'white': ethnic = 'Wh'; break;
|
|
242
|
+
case 'nonwhite': ethnic = 'Wh'; bInvert = true; break;
|
|
243
|
+
case 'black': ethnic = dataset.endsWith('NH') ? 'Bl' : 'BlC'; break;
|
|
244
|
+
case 'hisp': ethnic = 'His'; break;
|
|
245
|
+
case 'native': ethnic = dataset.endsWith('NH') ? 'Nat' : 'NatC'; break;
|
|
246
|
+
case 'asianpi': ethnic = 'AsnPI'; break;
|
|
247
|
+
case 'asian': ethnic = dataset.endsWith('NH') ? 'Asn' : 'AsnC'; break;
|
|
248
|
+
case 'pac': ethnic = dataset.endsWith('NH') ? 'Pac' : 'PacC'; break;
|
|
249
|
+
case 'other': ethnic = 'OthAl'; break;
|
|
250
|
+
case 'mix': ethnic = 'Mix'; break;
|
|
251
|
+
default: break;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const getter = PF.ToGetter(agg, dc, dataset);
|
|
255
|
+
let den = getter(total);
|
|
256
|
+
let num = getter(ethnic);
|
|
257
|
+
if (den === undefined || isNaN(den) || num === undefined || isNaN(num))
|
|
258
|
+
return '#ffffff';
|
|
259
|
+
|
|
260
|
+
if (den == 0)
|
|
261
|
+
return '#ffffff';
|
|
262
|
+
const pct = bInvert ? 1 - (num / den) : num / den;
|
|
263
|
+
return Util.execGradient(ethnicStops(EthnicFewStops, pd), pct);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// All Groups Mosaic
|
|
267
|
+
export function AggregateEthnicColor(getter: PF.FieldGetter, pd: PaletteDefaults, nhAlone: boolean): number
|
|
268
|
+
{
|
|
269
|
+
// Dataset should have 'Tot' field
|
|
270
|
+
let totField = 'Tot';
|
|
271
|
+
let totalPop = getter(totField);
|
|
272
|
+
if (totalPop == 0) return -1;
|
|
273
|
+
|
|
274
|
+
// Field names hard-coded
|
|
275
|
+
let white: number = getter('Wh') / totalPop;
|
|
276
|
+
let hisp: number = getter('His') / totalPop;
|
|
277
|
+
let black: number = 0;
|
|
278
|
+
let native: number = 0;
|
|
279
|
+
let asianPI: number = 0;
|
|
280
|
+
if (nhAlone)
|
|
281
|
+
{
|
|
282
|
+
black= getter('Bl') / totalPop;
|
|
283
|
+
native = getter('Nat') / totalPop;
|
|
284
|
+
asianPI = (getter('Asn') + getter('Pac')) / totalPop;
|
|
285
|
+
}
|
|
286
|
+
else
|
|
287
|
+
{
|
|
288
|
+
black= getter('BlC') / totalPop;
|
|
289
|
+
native = getter('NatC') / totalPop;
|
|
290
|
+
asianPI = (getter('AsnC') + getter('PacC')) / totalPop;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (white >= 0.75)
|
|
294
|
+
return ColorBySolidWhite;
|
|
295
|
+
if (black >= 0.75)
|
|
296
|
+
return ColorBySolidBlack;
|
|
297
|
+
if (hisp >= 0.75)
|
|
298
|
+
return ColorBySolidHispanic;
|
|
299
|
+
if (asianPI >= 0.75)
|
|
300
|
+
return ColorBySolidAsian;
|
|
301
|
+
if (white >= 0.5 && black < 0.3 && hisp < 0.3 && native < 0.3 && asianPI < 0.3)
|
|
302
|
+
return ColorByMostlyWhite;
|
|
303
|
+
if (black >= 0.5 && white < 0.3 && hisp < 0.3 && native < 0.3 && asianPI < 0.3)
|
|
304
|
+
return ColorByMostlyBlack;
|
|
305
|
+
if (hisp >= 0.5 && white < 0.3 && black < 0.3 && native < 0.3 && asianPI < 0.3)
|
|
306
|
+
return ColorByMostlyHispanic;
|
|
307
|
+
if (asianPI >= 0.5 && white < 0.3 && black < 0.3 && native < 0.3 && hisp < 0.3)
|
|
308
|
+
return ColorByMostlyAsian;
|
|
309
|
+
if (native > 0.5)
|
|
310
|
+
return ColorByMostlyNative;
|
|
311
|
+
if (hisp >= 0.3 && white >= 0.3 && hisp + white > 0.75)
|
|
312
|
+
return ColorByHispanicWhite;
|
|
313
|
+
if (black >= 0.3 && white >= 0.3 && black + white > 0.75)
|
|
314
|
+
return ColorByBlackWhite;
|
|
315
|
+
if (hisp >= 0.3 && black >= 0.3 && hisp + black > 0.75)
|
|
316
|
+
return ColorByHispanicBlack;
|
|
317
|
+
if (asianPI >= 0.3 && white >= 0.3 && asianPI + white > 0.75)
|
|
318
|
+
return ColorByAsianWhite;
|
|
319
|
+
if (asianPI >= 0.3 && hisp >= 0.3 && asianPI + hisp > 0.75)
|
|
320
|
+
return ColorByAsianHispanic;
|
|
321
|
+
if (black >= 0.3 && asianPI >= 0.3 && black + asianPI > 0.75)
|
|
322
|
+
return ColorByBlackAsian;
|
|
323
|
+
return ColorByMix;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// This is used only for 2016_BG analytics, seemingly only for values, not colors (dave 12/9/21)
|
|
327
|
+
export function AggregatePartisanColor(getter: PF.FieldGetter): number
|
|
328
|
+
{
|
|
329
|
+
// Dataset should have 'Tot' field
|
|
330
|
+
let totField = 'Tot';
|
|
331
|
+
let presTot = getter(totField);
|
|
332
|
+
if (presTot == 0) return -1;
|
|
333
|
+
|
|
334
|
+
// TODO: Field names hard-coded
|
|
335
|
+
let presR = getter('R');
|
|
336
|
+
let presD = getter(' D');
|
|
337
|
+
|
|
338
|
+
if (presTot === undefined || presD === undefined || presR === undefined || presTot <= 0)
|
|
339
|
+
return -1;
|
|
340
|
+
|
|
341
|
+
let pctD = presD / presTot;
|
|
342
|
+
let pctR = presR / presTot;
|
|
343
|
+
let diff = pctD - pctR;
|
|
344
|
+
if (diff < -0.20)
|
|
345
|
+
return ColorByGreatR;
|
|
346
|
+
if (diff < -0.10)
|
|
347
|
+
return ColorByGoodR;
|
|
348
|
+
if (diff < -0.02)
|
|
349
|
+
return ColorByFairR;
|
|
350
|
+
if (diff > 0.20)
|
|
351
|
+
return ColorByGreatD;
|
|
352
|
+
if (diff > 0.10)
|
|
353
|
+
return ColorByGoodD;
|
|
354
|
+
if (diff > 0.02)
|
|
355
|
+
return ColorByFairD;
|
|
356
|
+
return ColorByEven;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function AggregatePartisanColorStr(getter: PF.FieldGetter, stops: Util.GradientStops): string
|
|
360
|
+
{
|
|
361
|
+
// Dataset should have 'Tot' field
|
|
362
|
+
let totField = 'Tot';
|
|
363
|
+
let presTot = getter(totField);
|
|
364
|
+
if (presTot == 0) return '';
|
|
365
|
+
|
|
366
|
+
// TODO: Field names hard-coded
|
|
367
|
+
let presR = getter('R');
|
|
368
|
+
let presD = getter('D');
|
|
369
|
+
|
|
370
|
+
if (presTot === undefined || presD === undefined || presR === undefined || presTot <= 0)
|
|
371
|
+
return '';
|
|
372
|
+
|
|
373
|
+
let pctD: number = presD / presTot;
|
|
374
|
+
let pctR: number = presR / presTot;
|
|
375
|
+
let pctOth: number = (presTot - presD - presR) / presTot;
|
|
376
|
+
const color: string = ColorFromRGBPcts(pctR, pctOth, pctD, stops);
|
|
377
|
+
//console.log('Agg (r, d, color): (' + pctR + ', ' + pctD + ', ' + color + ')');
|
|
378
|
+
return color;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// This is the new gradient code
|
|
382
|
+
export function ColorFromRGBPcts(pctRed: number, pctGreen: number, pctBlue: number, stops: Util.GradientStops): string
|
|
383
|
+
{
|
|
384
|
+
let pctTot = pctRed + pctBlue;
|
|
385
|
+
if (pctTot == 0) return '#ffffff';
|
|
386
|
+
pctBlue /= pctTot;
|
|
387
|
+
return Util.execGradient(stops, pctBlue);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// ****************** End of "classic" palettes *****************
|
|
391
|
+
|
|
392
|
+
export type ColorUse = 'districts' | 'partisanScale' | 'demographicsScale' | 'demographicsAllGroups' | 'partisanDistrictsScale';
|
|
393
|
+
|
|
394
|
+
// Currently supported palettes
|
|
395
|
+
export const PaletteNames = ['jet_r', 'turbo_r', 'inferno_r', 'viridis_r', 'magma_r', 'plasma_r', 'Greys', 'bone_r',
|
|
396
|
+
'draclassic', 'demographicsclassic', 'partisanclassic', 'allgroupsclassic', 'partisandistrictsclassic'] as const;
|
|
397
|
+
export type PaletteName = typeof PaletteNames[number];
|
|
398
|
+
|
|
399
|
+
export function colorsFromStops(palette: PaletteName, stops: number[], classicColors: string[]): string[]
|
|
400
|
+
{
|
|
401
|
+
// Use classicColors to see where there are duplicate colors
|
|
402
|
+
const allColors: string[] = Colors.getPalette(palette);
|
|
403
|
+
let colors: string[] = [];
|
|
404
|
+
for (let i = 0; i < stops.length; i++)
|
|
405
|
+
{
|
|
406
|
+
const stop: number =
|
|
407
|
+
(i + 1 < classicColors.length && classicColors[i] == classicColors[i + 1]) ? (stops[i] + stops[i + 1]) / 2 :
|
|
408
|
+
(i > 0 && i < classicColors.length && classicColors[i - 1] == classicColors[i]) ? (stops[i - 1] + stops[i]) / 2 : stops[i];
|
|
409
|
+
|
|
410
|
+
let inx: number = Math.floor(allColors.length * stop);
|
|
411
|
+
if (inx >= allColors.length) inx = allColors.length - 1;
|
|
412
|
+
colors.push(allColors[inx]);
|
|
413
|
+
}
|
|
414
|
+
return colors;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function colorsFromStopsPartisan(palette: PaletteName, colorUse: ColorUse, stops: number[]): string[]
|
|
418
|
+
{
|
|
419
|
+
if (palette === 'partisanclassic')
|
|
420
|
+
return Colors.PartisanPrecinctClassicColors;
|
|
421
|
+
if (palette === 'partisandistrictsclassic')
|
|
422
|
+
return Colors.PartisanDistrictClassicColors;
|
|
423
|
+
|
|
424
|
+
// For partisanScale and partisanDistrictsScale; indexes match classic color pattern
|
|
425
|
+
const allColors: string[] = Colors.getPalette(palette);
|
|
426
|
+
if (allColors.length < Colors.MaxColors)
|
|
427
|
+
return Colors.PartisanPrecinctClassicColors; // Safety check; shouldn't happen
|
|
428
|
+
|
|
429
|
+
let colors: string[] = [];
|
|
430
|
+
|
|
431
|
+
const indexes: number[] = colorUse === 'partisanScale' ?
|
|
432
|
+
[15, 27, 37, 47, 57, 67, 82, 92, 102, 112, 122, 134] : [15, 15, 37, 37, 47, 67, 82, 102, 122, 122, 134, 134];
|
|
433
|
+
for (let i = 0; i < stops.length; i++)
|
|
434
|
+
{
|
|
435
|
+
colors.push(allColors[indexes[i]]);
|
|
436
|
+
}
|
|
437
|
+
return colors;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function allGroups16Colors(palette: PaletteName, colors: string[])
|
|
441
|
+
{
|
|
442
|
+
let modColors: string[] = [];
|
|
443
|
+
//if (palette === 'allgroupsclassic') // Only support classic for now
|
|
444
|
+
{
|
|
445
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorBySolidWhite]); // pos 0
|
|
446
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorBySolidBlack]); // pos 1
|
|
447
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorBySolidHispanic]);// pos 2
|
|
448
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorBySolidAsian]); // pos 3
|
|
449
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByMostlyWhite]); // pos 4
|
|
450
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByMostlyBlack]); // pos 5
|
|
451
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByMostlyHispanic]);// pos 6
|
|
452
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByMostlyAsian]); // pos 7
|
|
453
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByMostlyNative]); // pos 8
|
|
454
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByMix]); // pos 9
|
|
455
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByHispanicWhite]);// pos 10
|
|
456
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByBlackWhite]); // pos 11
|
|
457
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByHispanicBlack]);// pos 12
|
|
458
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByAsianWhite]); // pos 13
|
|
459
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByAsianHispanic]);// pos 14
|
|
460
|
+
modColors.push(Colors.EthnicBackgroundColor[ColorByBlackAsian]); // pos 15
|
|
461
|
+
}
|
|
462
|
+
/*else if (palette === 'viridis_r' || palette === 'plasma_r')
|
|
463
|
+
{
|
|
464
|
+
modColors.push(colors[149]);
|
|
465
|
+
modColors.push(colors[129]);
|
|
466
|
+
modColors.push(colors[109]);
|
|
467
|
+
modColors.push(colors[89]);
|
|
468
|
+
modColors.push(colors[139]);
|
|
469
|
+
modColors.push(colors[119]);
|
|
470
|
+
modColors.push(colors[99]);
|
|
471
|
+
modColors.push(colors[79]);
|
|
472
|
+
modColors.push(colors[0]);
|
|
473
|
+
modColors.push(colors[9]);
|
|
474
|
+
modColors.push(colors[29]);
|
|
475
|
+
modColors.push(colors[19]);
|
|
476
|
+
modColors.push(colors[49]);
|
|
477
|
+
modColors.push(colors[39]);
|
|
478
|
+
modColors.push(colors[69]);
|
|
479
|
+
modColors.push(colors[59]);
|
|
480
|
+
}
|
|
481
|
+
else if (palette === 'magma_r')
|
|
482
|
+
{
|
|
483
|
+
modColors.push(colors[134]);
|
|
484
|
+
modColors.push(colors[116]);
|
|
485
|
+
modColors.push(colors[98]);
|
|
486
|
+
modColors.push(colors[80]);
|
|
487
|
+
modColors.push(colors[125]);
|
|
488
|
+
modColors.push(colors[107]);
|
|
489
|
+
modColors.push(colors[89]);
|
|
490
|
+
modColors.push(colors[71]);
|
|
491
|
+
modColors.push(colors[0]);
|
|
492
|
+
modColors.push(colors[8]);
|
|
493
|
+
modColors.push(colors[26]);
|
|
494
|
+
modColors.push(colors[17]);
|
|
495
|
+
modColors.push(colors[44]);
|
|
496
|
+
modColors.push(colors[35]);
|
|
497
|
+
modColors.push(colors[62]);
|
|
498
|
+
modColors.push(colors[53]);
|
|
499
|
+
}*/
|
|
500
|
+
return modColors;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export interface DistrictCache
|
|
504
|
+
{
|
|
505
|
+
colorElection?: string;
|
|
506
|
+
colorEthnic?: string;
|
|
507
|
+
colorSolid?: string;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Color property we need out of the full DistrictProps
|
|
511
|
+
export interface ColorProp
|
|
512
|
+
{
|
|
513
|
+
color: string,
|
|
514
|
+
}
|
|
515
|
+
export type ColorProps = Array<ColorProp>;
|
|
516
|
+
|
|
517
|
+
export interface DistrictColorParams
|
|
518
|
+
{
|
|
519
|
+
datasetContext: PF.DatasetContext,
|
|
520
|
+
mapColors: ColorProps,
|
|
521
|
+
aggregates: PF.PackedFields[],
|
|
522
|
+
paletteDefaults: PaletteDefaults,
|
|
523
|
+
useFirstColor: boolean,
|
|
524
|
+
usePalette: string,
|
|
525
|
+
colorDistrictsBy: string,
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export function computeDistrictColors(params: DistrictColorParams): DistrictCache[]
|
|
529
|
+
{
|
|
530
|
+
let dcNew: DistrictCache[] = [];
|
|
531
|
+
for (let i: number = 0; i < params.aggregates.length; i++)
|
|
532
|
+
{
|
|
533
|
+
let agg = params.aggregates[i];
|
|
534
|
+
let mapColor = i < params.mapColors.length ? params.mapColors[i].color : Colors.genColor(i, params.useFirstColor, params.usePalette);
|
|
535
|
+
let dc: DistrictCache = {};
|
|
536
|
+
|
|
537
|
+
// Compute election color and partisan color
|
|
538
|
+
dc.colorEthnic = mapColor;
|
|
539
|
+
dc.colorElection = ToPartisanDistrictColor(agg, params.datasetContext, params.paletteDefaults);
|
|
540
|
+
|
|
541
|
+
switch (params.colorDistrictsBy)
|
|
542
|
+
{
|
|
543
|
+
case 'election':
|
|
544
|
+
dc.colorSolid = dc.colorElection;
|
|
545
|
+
break;
|
|
546
|
+
case 'nonwhite':
|
|
547
|
+
case 'hisp':
|
|
548
|
+
case 'black':
|
|
549
|
+
case 'native':
|
|
550
|
+
case 'asian':
|
|
551
|
+
case 'pac':
|
|
552
|
+
case 'all':
|
|
553
|
+
dc.colorEthnic = ToEthnicColorStr(agg, params.datasetContext, params.paletteDefaults, params.colorDistrictsBy);
|
|
554
|
+
dc.colorSolid = dc.colorEthnic;
|
|
555
|
+
break;
|
|
556
|
+
default:
|
|
557
|
+
dc.colorSolid = mapColor;
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
dcNew.push(dc);
|
|
561
|
+
}
|
|
562
|
+
return dcNew;
|
|
563
|
+
}
|
package/lib/dra-types.ts
CHANGED
|
@@ -42,6 +42,7 @@ export interface UserLikes
|
|
|
42
42
|
// congress, upper, lower and other => the plan is for entire state
|
|
43
43
|
// county and city => plan is for a single county or city within a state
|
|
44
44
|
export type PlanType = 'congress' | 'upper' | 'lower' | 'county' | 'city' | 'coi' | 'other';
|
|
45
|
+
export type PlanTypeOrEmpty = PlanType | '';
|
|
45
46
|
|
|
46
47
|
// Once we attempt to contact, they've optedin or optedout (both mean email is valid and not blocked, even if not "validated")
|
|
47
48
|
// implicitoptedin means we contacted them successfully and they didn't optout, but also did not explicitly optin
|