@dra2020/district-analytics 1.0.7 → 1.0.8

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.
Files changed (132) hide show
  1. package/README.md +5 -5
  2. package/dist/district-analytics.js +1315 -1205
  3. package/package.json +6 -3
  4. package/.prettierrc +0 -5
  5. package/dist/_api.d.ts +0 -27
  6. package/dist/_data.d.ts +0 -130
  7. package/dist/analyze.d.ts +0 -4
  8. package/dist/cli.js +0 -12091
  9. package/dist/cli.js.map +0 -1
  10. package/dist/cohesive.d.ts +0 -4
  11. package/dist/compact.d.ts +0 -5
  12. package/dist/constants.d.ts +0 -6
  13. package/dist/district-analytics.js.map +0 -1
  14. package/dist/equal.d.ts +0 -4
  15. package/dist/geofeature.d.ts +0 -3
  16. package/dist/index.d.ts +0 -2
  17. package/dist/minority.d.ts +0 -3
  18. package/dist/political.d.ts +0 -8
  19. package/dist/preprocess.d.ts +0 -2
  20. package/dist/report.d.ts +0 -15
  21. package/dist/settings.d.ts +0 -5
  22. package/dist/src/_api.d.ts +0 -27
  23. package/dist/src/_data.d.ts +0 -130
  24. package/dist/src/analyze.d.ts +0 -4
  25. package/dist/src/cohesive.d.ts +0 -4
  26. package/dist/src/compact.d.ts +0 -5
  27. package/dist/src/constants.d.ts +0 -6
  28. package/dist/src/equal.d.ts +0 -4
  29. package/dist/src/geofeature.d.ts +0 -3
  30. package/dist/src/index.d.ts +0 -2
  31. package/dist/src/minority.d.ts +0 -3
  32. package/dist/src/political.d.ts +0 -8
  33. package/dist/src/preprocess.d.ts +0 -2
  34. package/dist/src/report.d.ts +0 -15
  35. package/dist/src/settings.d.ts +0 -5
  36. package/dist/src/types.d.ts +0 -110
  37. package/dist/src/utils.d.ts +0 -28
  38. package/dist/src/valid.d.ts +0 -8
  39. package/dist/test/_cli.d.ts +0 -5
  40. package/dist/types.d.ts +0 -110
  41. package/dist/utils.d.ts +0 -28
  42. package/dist/valid.d.ts +0 -8
  43. package/jestconfig.json +0 -14
  44. package/lib/HelloWorld.d.ts +0 -3
  45. package/lib/HelloWorld.js +0 -11
  46. package/lib/_api.js +0 -91
  47. package/lib/_api.js.map +0 -1
  48. package/lib/_cli.js +0 -434
  49. package/lib/_cli.js.map +0 -1
  50. package/lib/_data.js +0 -425
  51. package/lib/_data.js.map +0 -1
  52. package/lib/analyze.d.ts +0 -3
  53. package/lib/analyze.js +0 -69
  54. package/lib/analyze.js.map +0 -1
  55. package/lib/api.d.ts +0 -34
  56. package/lib/api.js +0 -117
  57. package/lib/api.js.map +0 -1
  58. package/lib/cli.d.ts +0 -1
  59. package/lib/cli.js +0 -386
  60. package/lib/cli.js.map +0 -1
  61. package/lib/cohesive.d.ts +0 -4
  62. package/lib/cohesive.js +0 -132
  63. package/lib/cohesive.js.map +0 -1
  64. package/lib/compact.d.ts +0 -4
  65. package/lib/compact.js +0 -183
  66. package/lib/compact.js.map +0 -1
  67. package/lib/constants.js +0 -367
  68. package/lib/constants.js.map +0 -1
  69. package/lib/data.js +0 -188
  70. package/lib/data.js.map +0 -1
  71. package/lib/equal.d.ts +0 -4
  72. package/lib/equal.js +0 -59
  73. package/lib/equal.js.map +0 -1
  74. package/lib/features.js +0 -19
  75. package/lib/features.js.map +0 -1
  76. package/lib/geofeature.js +0 -112
  77. package/lib/geofeature.js.map +0 -1
  78. package/lib/index.d.ts +0 -5
  79. package/lib/index.js +0 -11
  80. package/lib/index.js.map +0 -1
  81. package/lib/minority.d.ts +0 -3
  82. package/lib/minority.js +0 -61
  83. package/lib/minority.js.map +0 -1
  84. package/lib/political.d.ts +0 -7
  85. package/lib/political.js +0 -88
  86. package/lib/political.js.map +0 -1
  87. package/lib/preprocess.d.ts +0 -4
  88. package/lib/preprocess.js +0 -101
  89. package/lib/preprocess.js.map +0 -1
  90. package/lib/report.d.ts +0 -14
  91. package/lib/report.js +0 -817
  92. package/lib/report.js.map +0 -1
  93. package/lib/sample.d.ts +0 -1
  94. package/lib/sample.js +0 -32
  95. package/lib/sample.js.map +0 -1
  96. package/lib/scorecard.d.ts +0 -4
  97. package/lib/scorecard.js +0 -237
  98. package/lib/scorecard.js.map +0 -1
  99. package/lib/settings.d.ts +0 -5
  100. package/lib/settings.js +0 -18
  101. package/lib/settings.js.map +0 -1
  102. package/lib/types.d.ts +0 -125
  103. package/lib/types.js +0 -7
  104. package/lib/types.js.map +0 -1
  105. package/lib/utils.d.ts +0 -20
  106. package/lib/utils.js +0 -223
  107. package/lib/utils.js.map +0 -1
  108. package/lib/valid.d.ts +0 -5
  109. package/lib/valid.js +0 -230
  110. package/lib/valid.js.map +0 -1
  111. package/main.js +0 -4
  112. package/src/_api.ts +0 -121
  113. package/src/_data.ts +0 -595
  114. package/src/analyze.ts +0 -92
  115. package/src/cohesive.ts +0 -156
  116. package/src/compact.ts +0 -208
  117. package/src/constants.ts +0 -371
  118. package/src/equal.ts +0 -75
  119. package/src/geofeature.ts +0 -138
  120. package/src/index.ts +0 -6
  121. package/src/minority.ts +0 -70
  122. package/src/political.ts +0 -114
  123. package/src/preprocess.ts +0 -132
  124. package/src/report.ts +0 -1022
  125. package/src/settings.ts +0 -20
  126. package/src/types.ts +0 -185
  127. package/src/utils.ts +0 -245
  128. package/src/valid.ts +0 -275
  129. package/tsconfig.json +0 -25
  130. package/tslint.json +0 -3
  131. package/types/polygon-clipping/index.d.ts +0 -1
  132. package/webpack.config.js +0 -73
package/src/_data.ts DELETED
@@ -1,595 +0,0 @@
1
- //
2
- // DATA ABSTRACTION LAYER
3
- //
4
-
5
- import * as T from './types'
6
- import * as U from './utils';
7
- import * as S from './settings';
8
-
9
- import { AnalyticsSession } from './_api';
10
- import { isConnected, isEmbedded } from './valid';
11
- import { extractDistrictProperties } from './compact';
12
- import { fptpWin } from './political'
13
-
14
-
15
- // DISTRICT STATISTICS
16
-
17
- // Indexes for statistics fields in Districts
18
- // NOTE - Not a const, so the number can be determined dynamically
19
- export enum DistrictField {
20
- TotalPop, // Display
21
- PopDevPct, // Display
22
- bEqualPop, // Display
23
- bNotEmpty, // Display
24
- bContiguous, // Display
25
- bNotEmbedded, // Display
26
- CountySplits,
27
- DemVotes,
28
- RepVotes,
29
- TwoPartyVote, // Two-party total ()= Dem + Rep) not all votes!
30
- DemPct, // Display (also the "VPI," by convention")
31
- RepPct, // Display
32
- DemSeat,
33
- TotalVAP, // VAP or CVAP ...
34
- MinorityPop, // Derived
35
- WhitePop,
36
- BlackPop,
37
- HispanicPop,
38
- PacificPop,
39
- AsianPop,
40
- NativePop,
41
- WhitePct, // Display
42
- MinorityPct, // Display
43
- BlackPct, // Display
44
- HispanicPct, // Display
45
- PacificPct, // Display
46
- AsianPct, // Display
47
- NativePct // Display
48
-
49
- // 1 - MORE ...
50
- }
51
-
52
- // The fields to display in a District Statistics pane
53
- export const DisplayFields = [
54
- DistrictField.TotalPop,
55
- DistrictField.PopDevPct,
56
- DistrictField.bEqualPop,
57
- DistrictField.bNotEmpty,
58
- DistrictField.bContiguous,
59
- DistrictField.bNotEmbedded,
60
- DistrictField.DemPct,
61
- DistrictField.WhitePct,
62
- DistrictField.MinorityPct,
63
- DistrictField.BlackPct,
64
- DistrictField.HispanicPct,
65
- DistrictField.PacificPct,
66
- DistrictField.AsianPct,
67
- DistrictField.NativePct
68
- ];
69
-
70
- export class Districts {
71
- _session: AnalyticsSession;
72
-
73
- _shapes: T.GeoFeatureCollection;
74
- _geoProperties = {} as T.DistrictProperties;
75
-
76
- statistics: any[][];
77
-
78
- constructor(s: AnalyticsSession, ds: T.GeoFeatureCollection) {
79
- this._session = s;
80
-
81
- this._shapes = ds;
82
- this.statistics = this.initStatistics();
83
- }
84
- getShape(i: number): T.GeoFeature { return this._shapes.features[i]; }
85
- getGeoProperties(i: number): any { return this._geoProperties[i]; }
86
- setGeoProperties(i: number, p: T.DistrictShapeProperties): void { this._geoProperties[i] = p; }
87
-
88
- numberOfColumns(): number { return U.countEnumValues(DistrictField); }
89
- // +1 for dummy unassigned 0 "district" and +1 for N+1 summary "district" for
90
- // state-level values. Real districts are 1–N.
91
- numberOfRows(): number { return this._session.state.nDistricts + 2; }
92
- numberOfWorkingDistricts(): number { return this._session.state.nDistricts + 1; }
93
-
94
- // This is the core statistics 2D matrix:
95
- // Fields on the outside, districts on the inside
96
- initStatistics(): any[][] {
97
- let nRows = this.numberOfRows();
98
- let nCols = this.numberOfColumns();
99
-
100
- let outer = U.initArray(nCols, undefined);
101
-
102
- for (let i = 0; i < nCols; i++) {
103
- outer[i] = U.initArray(nRows, null);
104
- }
105
-
106
- return outer;
107
- }
108
-
109
- // This is the workhorse computational routine!
110
- //
111
- // TODO - Optimize for getting multiple properties from the same feature?
112
- // TODO - Optimize by only re-calc'ing districts that have changed?
113
- // In this case, special attention to getting county-splits right.
114
- // TODO - Optimize by asyn'ing this?
115
- // TODO - Is there a way to do this programmatically off data? Does it matter?
116
- recalcStatistics(bLog: boolean = false): void {
117
- // Compute these once per recalc cycle
118
- let targetSize = this._session.state.totalPop / this._session.state.nDistricts;
119
- let deviationThreshold = this._session.populationDeviationThreshold();
120
- let planByDistrict = this._session.plan.byDistrictID();
121
- let plan = this._session.plan;
122
- let graph = this._session.graph;
123
-
124
- // INITIALIZE STATE VALUES THAT WILL BE ACCUMULATED
125
-
126
- let stateTPVote = 0;
127
- let stateDemVote = 0;
128
- let stateRepVote = 0;
129
-
130
- let stateVAPPop = 0;
131
- let stateWhitePop = 0;
132
- let stateMinorityPop = 0;
133
- let stateBlackPop = 0;
134
- let stateHispanicPop = 0;
135
- let statePacificPop = 0;
136
- let stateAsianPop = 0;
137
- let stateNativePop = 0;
138
-
139
- // NOTE - These plan-level booleans are set in their respective analytics:
140
- // - Equal population (bEqualPop)
141
- // - Complete (bNotEmpty)
142
- // - Contiguos (bContiguous)
143
- // - Free of holes (bNotEmbedded)
144
-
145
- // 2 - MORE ...
146
-
147
- // Loop over the districts (including the dummy unassigned one)
148
- for (let i = 0; i < this.numberOfWorkingDistricts(); i++) {
149
-
150
- // INITIALIZE DISTRICT VALUES THAT WILL BE ACCUMULATED (VS. DERIVED)
151
-
152
- let featurePop: number;
153
- let totalPop: number = 0;
154
-
155
- let countySplits: number[] = U.initArray(this._session.counties.nCounties, 0)
156
-
157
- let demVotes: number = 0;
158
- let repVotes: number = 0;
159
-
160
- let totalVAP: number = 0;
161
- let whitePop: number = 0;
162
- let blackPop: number = 0;
163
- let hispanicPop: number = 0;
164
- let pacificPop: number = 0;
165
- let asianPop: number = 0;
166
- let nativePop: number = 0;
167
-
168
- // 3 - MORE ...
169
-
170
- // HACK - Because "this" gets ghosted inside the forEach loop below
171
- let outerThis = this;
172
-
173
- // Get the geoIDs assigned to it ...
174
- let geoIDs = this._session.plan.geoIDsForDistrictID(i);
175
-
176
- // ... loop over them creating district-by-district statistics
177
- geoIDs.forEach(function (geoID: string): void {
178
- // Map from geoID to feature index
179
- let featureID = outerThis._session.features.featureID(geoID);
180
- let f: T.GeoFeature = outerThis._session.features.featureByIndex(featureID);
181
-
182
- // ACCUMULATE VALUES
183
-
184
- // Total population of each feature (used more than once)
185
- featurePop = outerThis._session.features.fieldForFeature(f, Dataset.CENSUS, FeatureField.TotalPop);
186
-
187
- // Total district population
188
- totalPop += featurePop;
189
-
190
- // Total population by counties w/in a district
191
- countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
192
-
193
- // Democratic and Republican vote totals
194
- demVotes += outerThis._session.features.fieldForFeature(f, Dataset.ELECTION, FeatureField.DemVotes);
195
- repVotes += outerThis._session.features.fieldForFeature(f, Dataset.ELECTION, FeatureField.RepVotes);
196
-
197
- // Voting-age demographic breakdowns (or citizen voting-age)
198
- // Guard againt null/NaN values
199
- let _totalVAP = outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.TotalPop);
200
- let _whitePop = outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.WhitePop);
201
- let _blackPop = outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.BlackPop);
202
- let _hispanicPop = outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.HispanicPop);
203
- let _pacificPop = outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.PacificPop);
204
- let _asianPop = outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.AsianPop);
205
- let _nativePop = outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.NativePop);
206
-
207
- if (_totalVAP) totalVAP += _totalVAP;
208
- if (_whitePop) whitePop += _whitePop;
209
- if (_blackPop) blackPop += _blackPop;
210
- if (_hispanicPop) hispanicPop += _hispanicPop;
211
- if (_pacificPop) pacificPop += _pacificPop;
212
- if (_asianPop) asianPop += _asianPop;
213
- if (_nativePop) nativePop += _nativePop;
214
-
215
- // TODO - DELETE
216
- // totalVAP += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.TotalPop);
217
- // whitePop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.WhitePop);
218
- // blackPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.BlackPop);
219
- // hispanicPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.HispanicPop);
220
- // pacificPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.PacificPop);
221
- // asianPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.AsianPop);
222
- // nativePop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.NativePop);
223
-
224
- // 4 - MORE ...
225
- });
226
-
227
- // COMPUTE DERIVED VALUES
228
-
229
- // Population deviation % and equal population (boolean) by district.
230
- // Leave the values null for the dummy unassigned district.
231
- let popDevPct = null;
232
- let bEqualPop = null;
233
- if (i > 0) {
234
- popDevPct = (totalPop - targetSize) / targetSize;
235
- bEqualPop = (popDevPct <= deviationThreshold);
236
- }
237
-
238
- // Total two-party (not total total!) votes, Democratic and Republican vote
239
- // shares, and Democratic first-past-the-post win (= 1) or loss (= 0).
240
- let totVotes: number;
241
- let demPct: number = 0;
242
- let repPct: number = 0;
243
- let DemSeat: number = 0;
244
-
245
- totVotes = demVotes + repVotes;
246
- if (totVotes > 0) {
247
- demPct = demVotes / totVotes;
248
- repPct = repVotes / totVotes;
249
- DemSeat = fptpWin(demPct);
250
- }
251
-
252
- // Total minority VAP
253
- let minorityPop = totalVAP - whitePop
254
-
255
- // Voting-age demographic proportions (or citizen voting-age)
256
- let whitePct: number = 0;
257
- let minorityPct: number = 0;
258
- let blackPct: number = 0;
259
- let hispanicPct: number = 0;
260
- let pacificPct: number = 0;
261
- let asianPct: number = 0;
262
- let nativePct: number = 0;
263
-
264
- if (totalVAP > 0) {
265
- whitePct = whitePop / totalVAP;
266
- minorityPct = minorityPop / totalVAP;
267
- blackPct = blackPop / totalVAP;
268
- hispanicPct = hispanicPop / totalVAP;
269
- pacificPct = pacificPop / totalVAP;
270
- asianPct = asianPop / totalVAP;
271
- nativePct = nativePop / totalVAP;
272
- }
273
-
274
- // 5 - MORE ...
275
-
276
- // COMPUTE DISTRICT-LEVEL VALUES
277
-
278
- // Validations
279
- let bNotEmpty = (!U.isSetEmpty(geoIDs));
280
- let bContiguous = null;
281
- let bNotEmbedded = null;
282
- // Leave the values null for the dummy unassigned district,
283
- // and districts that are empty.
284
- if (i > 0) {
285
- if (bNotEmpty) {
286
- bContiguous = isConnected(geoIDs, graph);
287
- bNotEmbedded = (!isEmbedded(i, planByDistrict[i], plan, graph));
288
- }
289
- }
290
-
291
- // 6 - MORE ...
292
-
293
- // UPDATE THE DISTRICT STATISTICS
294
-
295
- this.statistics[DistrictField.bNotEmpty][i] = bNotEmpty;
296
- this.statistics[DistrictField.bContiguous][i] = bContiguous;
297
- this.statistics[DistrictField.bNotEmbedded][i] = bNotEmbedded;
298
-
299
- this.statistics[DistrictField.TotalPop][i] = totalPop;
300
- this.statistics[DistrictField.PopDevPct][i] = popDevPct;
301
- this.statistics[DistrictField.bEqualPop][i] = bEqualPop;
302
-
303
- this.statistics[DistrictField.CountySplits][i] = countySplits;
304
-
305
- this.statistics[DistrictField.DemVotes][i] = demVotes;
306
- this.statistics[DistrictField.RepVotes][i] = repVotes;
307
- this.statistics[DistrictField.TwoPartyVote][i] = totVotes;
308
- this.statistics[DistrictField.DemPct][i] = demPct;
309
- this.statistics[DistrictField.RepPct][i] = repPct;
310
- this.statistics[DistrictField.DemSeat][i] = DemSeat;
311
-
312
- this.statistics[DistrictField.WhitePop][i] = whitePop;
313
- this.statistics[DistrictField.MinorityPop][i] = minorityPop;
314
- this.statistics[DistrictField.BlackPop][i] = blackPop;
315
- this.statistics[DistrictField.HispanicPop][i] = hispanicPop;
316
- this.statistics[DistrictField.PacificPop][i] = pacificPop;
317
- this.statistics[DistrictField.AsianPop][i] = asianPop;
318
- this.statistics[DistrictField.NativePop][i] = nativePop;
319
-
320
- this.statistics[DistrictField.TotalVAP][i] = totalVAP;
321
- this.statistics[DistrictField.WhitePct][i] = whitePct;
322
- this.statistics[DistrictField.MinorityPct][i] = minorityPct;
323
- this.statistics[DistrictField.BlackPct][i] = blackPct;
324
- this.statistics[DistrictField.HispanicPct][i] = hispanicPct;
325
- this.statistics[DistrictField.PacificPct][i] = pacificPct;
326
- this.statistics[DistrictField.AsianPct][i] = asianPct;
327
- this.statistics[DistrictField.NativePct][i] = nativePct;
328
-
329
- // 7 - MORE ...
330
-
331
- // ACCUMULATE STATE STATISTICS FROM DISTRICT TOTALS
332
-
333
- stateTPVote += totVotes;
334
- stateDemVote += demVotes;
335
- stateRepVote += repVotes;
336
-
337
- stateVAPPop += totalVAP;
338
- stateWhitePop += whitePop;
339
- stateMinorityPop += minorityPop
340
- stateBlackPop += blackPop;
341
- stateHispanicPop += hispanicPop;
342
- statePacificPop += pacificPop;
343
- stateAsianPop += asianPop;
344
- stateNativePop += nativePop;
345
- }
346
-
347
- // UPDATE STATE STATISTICS
348
- let summaryRow = this.numberOfRows() - 1;
349
-
350
- if (stateTPVote > 0) {
351
- this.statistics[DistrictField.DemPct][summaryRow] = stateDemVote / stateTPVote;
352
- this.statistics[DistrictField.RepPct][summaryRow] = stateRepVote / stateTPVote;
353
- }
354
-
355
- if (stateVAPPop > 0) {
356
- this.statistics[DistrictField.WhitePct][summaryRow] = stateWhitePop / stateVAPPop;
357
- this.statistics[DistrictField.MinorityPct][summaryRow] = stateMinorityPop / stateVAPPop;
358
- this.statistics[DistrictField.BlackPct][summaryRow] = stateBlackPop / stateVAPPop;
359
- this.statistics[DistrictField.HispanicPct][summaryRow] = stateHispanicPop / stateVAPPop;
360
- this.statistics[DistrictField.PacificPct][summaryRow] = statePacificPop / stateVAPPop;
361
- this.statistics[DistrictField.AsianPct][summaryRow] = stateAsianPop / stateVAPPop;
362
- this.statistics[DistrictField.NativePct][summaryRow] = stateNativePop / stateVAPPop;
363
- }
364
- }
365
- // NOTE - I did not roll these into district statistics, because creating the
366
- // district shapes themselves is the big district-by-district activity, these
367
- // calc's already work, and I'm not going to expose these values. Wrapping
368
- // the underlying function and exposing it here to illustrate the parallelism
369
- // with recalcStatistics(). These are called in tandem by doAnalyzeDistricts().
370
- extractDistrictShapeProperties(bLog: boolean = false): void {
371
- extractDistrictProperties(this._session, bLog)
372
- }
373
- getCountyIndex(geoID: string): number {
374
- let countyGeoID = U.parseGeoID(geoID)['county'] as string;
375
- let countyFIPS = U.getFIPSFromCountyGeoID(countyGeoID);
376
- let countyIndex = this._session.counties.indexFromFIPS(countyFIPS);
377
-
378
- return countyIndex;
379
- }
380
- }
381
-
382
-
383
- // CLASSES, ETC. FOR FEATURE & COUNTY DATA
384
-
385
- // Types of datasets by feature
386
- export const enum Dataset {
387
- CENSUS = "CENSUS",
388
- VAP = "VAP",
389
- ELECTION = "ELECTION"
390
- }
391
-
392
- export type DatasetKeys = {
393
- CENSUS: string; // A total population Census dataset
394
- VAP: string; // A voting age (or citizen voting age) dataset
395
- ELECTION: string; // An election dataset
396
- }
397
-
398
- export const DatasetDescriptions: any = {
399
- D16F: "2016 ACS Total Population",
400
- D16T: "2016 ACS Voting Age Population",
401
- E16GPR: "2016 Presidential Election",
402
- D10F: "2010 Census Total Population",
403
- D10T: "2010 Voting Age Population",
404
- C16GCO: "2016 Presidential, US Senate, Governor, and AG election results"
405
-
406
- // TODO - What other potential datasets?
407
- // MORE ...
408
- }
409
-
410
- // Identifiers of fields for each feature in the datasets
411
- export const enum FeatureField {
412
- TotalPop = "Tot",
413
- WhitePop = "Wh",
414
- BlackPop = "BlC",
415
- HispanicPop = "His",
416
- AsianPop = "AsnC",
417
- PacificPop = "PacC",
418
- NativePop = "NatC",
419
- DemVotes = "D",
420
- RepVotes = "R",
421
- TotalVotes = "Tot"
422
- }
423
-
424
- // Wrap data by feature, to abstract the specifics of the internal structure
425
- export class Features {
426
- _session: AnalyticsSession;
427
-
428
- _data: T.GeoFeatureCollection;
429
- _keys: DatasetKeys;
430
-
431
- _featureIDs = {} as T.FeaturesByGeoID;
432
-
433
- constructor(s: AnalyticsSession, data: T.GeoFeatureCollection, keys: DatasetKeys) {
434
- this._session = s;
435
-
436
- this._data = data;
437
- this._keys = keys;
438
- }
439
- nFeatures(): number { return this._data.features.length; }
440
- featureByIndex(i: number): T.GeoFeature { return this._data.features[i]; }
441
- // TODO - Generalize this
442
- geoIDForFeature(f: any): string { return f.properties['GEOID10']; }
443
- fieldForFeature(f: any, dt: Dataset, fk: string): any {
444
- let dk: string = this._keys[dt];
445
-
446
- return _getFeatures(f, dk, fk);
447
- }
448
- resetDataset(d: Dataset, k: string): void {
449
- this._keys[d] = k;
450
- // TODO - Does anything need to be recalc'd now when a dataset is changed?
451
- }
452
- mapGeoIDsToFeatureIDs(): void {
453
- for (let i: number = 0; i < this._session.features.nFeatures(); i++) {
454
- let f: T.GeoFeature = this._session.features.featureByIndex(i);
455
- let geoID: string = this._session.features.geoIDForFeature(f);
456
-
457
- this._featureIDs[geoID] = i;
458
- }
459
- }
460
- featureID(i: string): number { return this._featureIDs[i]; }
461
- }
462
-
463
- // NOTE - This accessor is cloned from fGetW() in dra-client/restrict.ts
464
- // f is a direct GeoJSON feature
465
- // p is a geoID
466
- function _getFeatures(f: any, datasetKey: string, p: string): any {
467
- // Shim to load sample data2.json from disk for command-line scaffolding
468
- if (f.properties && f.properties['datasets']) {
469
- return f.properties['datasets'][datasetKey][p];
470
- }
471
-
472
- // NOTE - The fGetW() code from dra-client below here ...
473
-
474
- // Direct property?
475
- if (f.properties && f.properties[p] !== undefined)
476
- return f.properties[p];
477
-
478
- // Joined property?
479
- let a: any[] = _fGetJoined(f);
480
- if (a) {
481
- for (let i: number = 0; i < a.length; i++) {
482
- let o: any = a[i];
483
- if (!datasetKey) {
484
- if (o[p] !== undefined)
485
- return o[p];
486
- }
487
- else {
488
- if (o['datasets'] && o['datasets'][datasetKey])
489
- if (o['datasets'][datasetKey][p])
490
- return o['datasets'][datasetKey][p];
491
- }
492
- }
493
- }
494
- return undefined;
495
- }
496
-
497
- function _fGetJoined(f: any): any[] {
498
- return (f.properties && f.properties.joined) ? f.properties.joined : undefined;
499
- }
500
-
501
- // Wrap data by county, to abstract the specifics of the internal structure
502
- export class Counties {
503
- _session: AnalyticsSession;
504
-
505
- _data: T.GeoFeatureCollection;
506
- _countyNameLookup: T.FIPSCodeToCountyNameMap;
507
-
508
- nCounties: number;
509
- index = {} as T.FIPSToOrdinalMap;
510
-
511
- constructor(s: AnalyticsSession, data: T.GeoFeatureCollection) {
512
- this._session = s;
513
-
514
- this._data = data;
515
- this.nCounties = this._data.features.length;
516
- this._countyNameLookup = {};
517
- }
518
- // nCounties(): number { return this._data.features.length; }
519
- countyByIndex(i: number): T.GeoFeature { return this._data.features[i]; }
520
- propertyForCounty(f: any, pk: string): any { return f.properties[pk]; }
521
- mapFIPSToName(fips: string, name: string): void { this._countyNameLookup[fips] = name; }
522
- nameFromFIPS(fips: string): string { return this._countyNameLookup[fips]; }
523
- indexFromFIPS(fips: string): number { return this.index[fips]; }
524
- }
525
-
526
- // CLASSES TO ORGANIZE AND/OR ABSTRACT OTHER DATA
527
-
528
- export class State {
529
- _session: AnalyticsSession;
530
-
531
- xx: string;
532
- nDistricts: number;
533
- totalPop = 0;
534
- tooBigFIPS = [] as string[];
535
- tooBigName = [] as string[];
536
- expectedSplits = 0;
537
- expectedAffected = 0;
538
-
539
- constructor(s: AnalyticsSession, xx: string, n: number) {
540
- this._session = s;
541
-
542
- this.xx = xx;
543
- this.nDistricts = n;
544
- }
545
- }
546
-
547
- export class Plan {
548
- _session: AnalyticsSession;
549
-
550
- // TODO - Do these need to be updated, when the plan changes?
551
- _planByGeoID: T.PlanByGeoID;
552
- _planByDistrictID: T.PlanByDistrictID;
553
-
554
- districtIDs: number[];
555
-
556
- constructor(s: AnalyticsSession, p: T.PlanByGeoID) {
557
- this._session = s;
558
-
559
- this._planByGeoID = p;
560
- this._planByDistrictID = {} as T.PlanByDistrictID;
561
-
562
- this.districtIDs = []; // Set when the plan in inverted
563
- }
564
- invertPlan(): void {
565
- this._planByDistrictID = U.invertPlan(this._planByGeoID);
566
- this.districtIDs = U.getNumericObjectKeys(this._planByDistrictID);
567
- }
568
- initializeDistrict(i: number): void { this._planByDistrictID[i] = new Set(); }
569
-
570
- byGeoID(): T.PlanByGeoID { return this._planByGeoID; }
571
- byDistrictID(): T.PlanByDistrictID { return this._planByDistrictID; }
572
- districtForGeoID(i: string): number { return this._planByGeoID[i]; }
573
- geoIDsForDistrictID(i: number): Set<string> { return this._planByDistrictID[i]; }
574
- }
575
-
576
- export class Graph {
577
- _session: AnalyticsSession;
578
-
579
- _graph: T.ContiguityGraph;
580
-
581
- constructor(s: AnalyticsSession, graph: T.ContiguityGraph) {
582
- this._session = s;
583
-
584
- this._graph = graph;
585
- }
586
- // TODO - Rework this, when we support MIXED MAPS.
587
- peerNeighbors(node: string): string[] {
588
- // Get the neighboring geoIDs connected to a geoID
589
- // Ignore the lengths of the shared borders (the values), for now
590
-
591
- return U.getObjectKeys(this._graph[node]);
592
- }
593
- }
594
-
595
-
package/src/analyze.ts DELETED
@@ -1,92 +0,0 @@
1
- //
2
- // ANALYZE A PLAN
3
- //
4
-
5
- import * as T from './types'
6
- import * as U from './utils';
7
- import * as S from './settings';
8
-
9
- import { AnalyticsSession } from './_api';
10
-
11
- import { doIsComplete, doIsContiguous, doIsFreeOfHoles } from './valid'
12
- import { doPopulationDeviation, doHasEqualPopulations } from './equal';
13
- import { doReock, doPolsbyPopper, extractDistrictProperties } from './compact';
14
- import { doCountySplits, doPlanComplexity } from './cohesive';
15
- import {
16
- doSeatsBias, doVotesBias,
17
- doResponsiveness, doResponsiveDistricts, doEfficiencyGap
18
- } from './political';
19
- import { doMajorityMinorityDistricts } from './minority'
20
-
21
- // Compile district-level info for plan/map-level analytics
22
- export function doAnalyzeDistricts(s: AnalyticsSession, bLog: boolean = false): void {
23
- s.districts.recalcStatistics(bLog);
24
- s.districts.extractDistrictShapeProperties(bLog);
25
- }
26
-
27
- // TODO - I could make this table-driven, but I'm thinking that the explicit
28
- // calls might make chunking for aync easier.
29
- // Calculate the analytics & validations and cache the results
30
- // NOTE - doAnalyzePlan() depends on doAnalyzeDistricts() having run first.
31
- export function doAnalyzePlan(s: AnalyticsSession, bLog: boolean = false): void {
32
- // Get the requested suites, and only execute those tests
33
- let requestedSuites = s.config['suites'];
34
-
35
- // Tests in the "Legal" suite, i.e., pass/ fail constraints
36
- if (requestedSuites.includes(T.Suite.Legal)) {
37
- s.tests[T.Test.Complete] = doIsComplete(s);
38
-
39
- s.tests[T.Test.Contiguous] = doIsContiguous(s);
40
-
41
- s.tests[T.Test.FreeOfHoles] = doIsFreeOfHoles(s);
42
-
43
- s.tests[T.Test.PopulationDeviation] = doPopulationDeviation(s);
44
-
45
- // NOTE - I can't check whether a population deviation is legal or not, until
46
- // the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
47
- // the given type of district (CD vs. LD). The EqualPopulation test is derived
48
- // from PopulationDeviation, as part of scorecard or test log preparation.
49
-
50
- // Create an empty test entry here though ...
51
- s.tests[T.Test.EqualPopulation] = s.getTest(T.Test.EqualPopulation) as T.TestEntry;
52
- }
53
-
54
- // Tests in the "Fair" suite
55
- if (requestedSuites.includes(T.Suite.Fair)) {
56
- s.tests[T.Test.SeatsBias] = doSeatsBias(s);
57
-
58
- s.tests[T.Test.VotesBias] = doVotesBias(s);
59
-
60
- s.tests[T.Test.Responsiveness] = doResponsiveness(s);
61
-
62
- s.tests[T.Test.ResponsiveDistricts] = doResponsiveDistricts(s);
63
-
64
- s.tests[T.Test.EfficiencyGap] = doEfficiencyGap(s);
65
-
66
- s.tests[T.Test.MajorityMinorityDistricts] = doMajorityMinorityDistricts(s);
67
- }
68
-
69
- // Tests in the "Best" suite, i.e., criteria for better/worse
70
- if (requestedSuites.includes(T.Suite.Best)) {
71
- s.tests[T.Test.Reock] = doReock(s, bLog);
72
- s.tests[T.Test.PolsbyPopper] = doPolsbyPopper(s, bLog);
73
-
74
- s.tests[T.Test.CountySplits] = doCountySplits(s);
75
-
76
- s.tests[T.Test.Complexity] = doPlanComplexity(s);
77
- }
78
-
79
- // Enable a Test Log and Scorecard to be generated
80
- s.bPlanAnalyzed = true;
81
- s.bPostProcessingDone = false;
82
- }
83
-
84
- // Derive secondary analytics that are based on primary tests.
85
- // This concept allows Population Deviation to be a primary numeric test and
86
- // Equal Population to be secondary pass/fail validation.
87
- //
88
- // NOTE - Should this be conditionalized on the test suites requested?
89
- // Those are encapsulated in reports.ts right now, so not doing that.
90
- export function doDeriveSecondaryTests(s: AnalyticsSession): void {
91
- s.tests[T.Test.EqualPopulation] = doHasEqualPopulations(s);
92
- }