@dra2020/district-analytics 9.3.0 → 9.4.1

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.
@@ -351,28 +351,31 @@ var DistrictField;
351
351
  DistrictField[DistrictField["bContiguous"] = 4] = "bContiguous";
352
352
  DistrictField[DistrictField["bNotEmbedded"] = 5] = "bNotEmbedded";
353
353
  DistrictField[DistrictField["CountySplits"] = 6] = "CountySplits";
354
+ // 10-22-2020 - Add OtherVotes
354
355
  DistrictField[DistrictField["DemVotes"] = 7] = "DemVotes";
355
356
  DistrictField[DistrictField["RepVotes"] = 8] = "RepVotes";
356
- DistrictField[DistrictField["TwoPartyVote"] = 9] = "TwoPartyVote";
357
+ DistrictField[DistrictField["OtherVotes"] = 9] = "OtherVotes";
358
+ // TwoPartyVote, // Two-party total ()= Dem + Rep) not all votes!
357
359
  DistrictField[DistrictField["DemPct"] = 10] = "DemPct";
358
360
  DistrictField[DistrictField["RepPct"] = 11] = "RepPct";
359
- DistrictField[DistrictField["DemSeat"] = 12] = "DemSeat";
360
- DistrictField[DistrictField["TotalVAP"] = 13] = "TotalVAP";
361
- DistrictField[DistrictField["MinorityPop"] = 14] = "MinorityPop";
362
- DistrictField[DistrictField["WhitePop"] = 15] = "WhitePop";
363
- DistrictField[DistrictField["BlackPop"] = 16] = "BlackPop";
364
- DistrictField[DistrictField["HispanicPop"] = 17] = "HispanicPop";
365
- DistrictField[DistrictField["PacificPop"] = 18] = "PacificPop";
366
- DistrictField[DistrictField["AsianPop"] = 19] = "AsianPop";
367
- DistrictField[DistrictField["NativePop"] = 20] = "NativePop";
368
- DistrictField[DistrictField["WhitePct"] = 21] = "WhitePct";
369
- DistrictField[DistrictField["MinorityPct"] = 22] = "MinorityPct";
370
- DistrictField[DistrictField["BlackPct"] = 23] = "BlackPct";
371
- DistrictField[DistrictField["HispanicPct"] = 24] = "HispanicPct";
372
- DistrictField[DistrictField["PacificPct"] = 25] = "PacificPct";
373
- DistrictField[DistrictField["AsianPct"] = 26] = "AsianPct";
374
- DistrictField[DistrictField["NativePct"] = 27] = "NativePct"; // Display
375
- // 1 - MORE ...
361
+ DistrictField[DistrictField["OtherPct"] = 12] = "OtherPct";
362
+ // End - OtherVotes changes
363
+ DistrictField[DistrictField["DemSeat"] = 13] = "DemSeat";
364
+ DistrictField[DistrictField["TotalVAP"] = 14] = "TotalVAP";
365
+ DistrictField[DistrictField["MinorityPop"] = 15] = "MinorityPop";
366
+ DistrictField[DistrictField["WhitePop"] = 16] = "WhitePop";
367
+ DistrictField[DistrictField["BlackPop"] = 17] = "BlackPop";
368
+ DistrictField[DistrictField["HispanicPop"] = 18] = "HispanicPop";
369
+ DistrictField[DistrictField["PacificPop"] = 19] = "PacificPop";
370
+ DistrictField[DistrictField["AsianPop"] = 20] = "AsianPop";
371
+ DistrictField[DistrictField["NativePop"] = 21] = "NativePop";
372
+ DistrictField[DistrictField["WhitePct"] = 22] = "WhitePct";
373
+ DistrictField[DistrictField["MinorityPct"] = 23] = "MinorityPct";
374
+ DistrictField[DistrictField["BlackPct"] = 24] = "BlackPct";
375
+ DistrictField[DistrictField["HispanicPct"] = 25] = "HispanicPct";
376
+ DistrictField[DistrictField["PacificPct"] = 26] = "PacificPct";
377
+ DistrictField[DistrictField["AsianPct"] = 27] = "AsianPct";
378
+ DistrictField[DistrictField["NativePct"] = 28] = "NativePct"; // Display
376
379
  })(DistrictField = exports.DistrictField || (exports.DistrictField = {}));
377
380
  class Districts {
378
381
  constructor(s, ds) {
@@ -419,12 +422,6 @@ class Districts {
419
422
  return outer;
420
423
  }
421
424
  // This is the workhorse computational routine!
422
- //
423
- // NOTE - OPTIMIZE for getting multiple properties from the same feature?
424
- // NOTE - OPTIMIZE by only re-calc'ing districts that have changed?
425
- // In this case, special attention to getting county-splits right.
426
- // NOTE - OPTIMIZE by async'ing this?
427
- // NOTE - Is there a way to do this programmatically off data? Does it matter?
428
425
  recalcStatistics(bLog = false) {
429
426
  // Initialize debug counters
430
427
  nMissingDataset = 0;
@@ -432,15 +429,18 @@ class Districts {
432
429
  // Compute these once per recalc cycle
433
430
  let targetSize = Math.round(this._session.state.totalPop / this._session.state.nDistricts);
434
431
  let deviationThreshold = this._session.populationDeviationThreshold();
435
- let planByDistrict = this._session.plan.byDistrictID();
432
+ // let planByDistrict = this._session.plan.byDistrictID();
436
433
  let plan = this._session.plan;
437
434
  let graph = this._session.graph;
438
435
  // Add an extra 0th virtual county bucket for county-district splitting analysis
439
436
  let nCountyBuckets = this._session.counties.nCounties + 1;
440
437
  // INITIALIZE STATE VALUES THAT WILL BE ACCUMULATED
441
- let stateTPVote = 0;
438
+ // 10-22-2020 - Add OtherVotes
442
439
  let stateDemVote = 0;
443
440
  let stateRepVote = 0;
441
+ let stateOthVote = 0;
442
+ let stateTotVote = 0;
443
+ // let stateTPVote = 0;
444
444
  let stateVAPPop = 0;
445
445
  let stateWhitePop = 0;
446
446
  let stateMinorityPop = 0;
@@ -454,15 +454,17 @@ class Districts {
454
454
  // - Complete (bNotEmpty)
455
455
  // - Contiguous (bContiguous)
456
456
  // - Free of holes (bNotEmbedded)
457
- // 2 - MORE ...
458
457
  // Loop over the districts (including the dummy unassigned one)
459
458
  for (let i = 0; i < this.numberOfWorkingDistricts(); i++) {
460
459
  // INITIALIZE DISTRICT VALUES THAT WILL BE ACCUMULATED (VS. DERIVED)
461
460
  let featurePop;
462
461
  let totalPop = 0;
463
462
  let countySplits = U.initArray(nCountyBuckets, 0);
463
+ // 10-22-2020 - Add OtherVotes
464
464
  let demVotes = 0;
465
465
  let repVotes = 0;
466
+ let othVotes = 0;
467
+ let totVotes = 0;
466
468
  let totalVAP = 0;
467
469
  let whitePop = 0;
468
470
  let blackPop = 0;
@@ -517,8 +519,13 @@ class Districts {
517
519
  console.log("County not recognized:", geoID);
518
520
  }
519
521
  // Democratic and Republican vote totals
522
+ // 10-22-2020 - Add OtherVotes
520
523
  demVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "D" /* DemVotes */);
521
524
  repVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "R" /* RepVotes */);
525
+ totVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "Tot" /* TotalVotes */);
526
+ // NOTE: Unless you grab the values above before accumulating them,
527
+ // you can't accumulate othVotes for districts. You must calculate
528
+ // them by implication later.
522
529
  // Voting-age demographic breakdowns (or citizen voting-age)
523
530
  totalVAP += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Tot" /* TotalPop */);
524
531
  whitePop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Wh" /* WhitePop */);
@@ -527,7 +534,6 @@ class Districts {
527
534
  pacificPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "PacC" /* PacificPop */);
528
535
  asianPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "AsnC" /* AsianPop */);
529
536
  nativePop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "NatC" /* NativePop */);
530
- // 4 - MORE ...
531
537
  }
532
538
  }
533
539
  else {
@@ -543,16 +549,18 @@ class Districts {
543
549
  bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
544
550
  }
545
551
  }
546
- // Total two-party (not total total!) votes, Democratic and Republican vote
547
- // shares, and Democratic first-past-the-post win (= 1) or loss (= 0).
548
- let totVotes;
552
+ // 10-22-2020 - Add OtherVotes. Revised from two-party to include Other.
553
+ // let totVotes: number; <<< Now being accumulated by district
549
554
  let demPct = 0;
550
555
  let repPct = 0;
556
+ let othPct = 0;
551
557
  let DemSeat = 0;
552
- totVotes = demVotes + repVotes;
558
+ // totVotes = demVotes + repVotes; <<< Now being accumulated by district
553
559
  if (totVotes > 0) {
560
+ othVotes = totVotes - demVotes - repVotes;
554
561
  demPct = demVotes / totVotes;
555
562
  repPct = repVotes / totVotes;
563
+ othPct = othVotes / totVotes;
556
564
  DemSeat = political_1.fptpWin(demPct);
557
565
  }
558
566
  // Total minority VAP
@@ -574,7 +582,6 @@ class Districts {
574
582
  asianPct = asianPop / totalVAP;
575
583
  nativePct = nativePop / totalVAP;
576
584
  }
577
- // 5 - MORE ...
578
585
  // COMPUTE DISTRICT-LEVEL VALUES
579
586
  // Validations
580
587
  // Leave the default values for the dummy unassigned district,
@@ -590,7 +597,6 @@ class Districts {
590
597
  bContiguous = G.isConnected(features, nakedGraph);
591
598
  bNotEmbedded = (!G.isEmbedded(i, features, nakedPlan, nakedGraph));
592
599
  }
593
- // 6 - MORE ...
594
600
  { // UPDATE THE DISTRICT STATISTICS
595
601
  // NOTE - These are set below for both non-empty & empty districts:
596
602
  // this.statistics[DistrictField.bNotEmpty][i] = bNotEmpty;
@@ -599,11 +605,14 @@ class Districts {
599
605
  // this.statistics[DistrictField.TotalPop][i] = totalPop;
600
606
  // this.statistics[DistrictField.bEqualPop][i] = bEqualPop;
601
607
  this.statistics[DistrictField.PopDevPct][i] = popDevPct;
608
+ // 10-22-2020 - Add OtherVotes
602
609
  this.statistics[DistrictField.DemVotes][i] = demVotes;
603
610
  this.statistics[DistrictField.RepVotes][i] = repVotes;
604
- this.statistics[DistrictField.TwoPartyVote][i] = totVotes;
611
+ this.statistics[DistrictField.OtherVotes][i] = othVotes;
612
+ // this.statistics[DistrictField.TwoPartyVote][i] = totVotes;
605
613
  this.statistics[DistrictField.DemPct][i] = demPct;
606
614
  this.statistics[DistrictField.RepPct][i] = repPct;
615
+ this.statistics[DistrictField.OtherPct][i] = othPct;
607
616
  this.statistics[DistrictField.DemSeat][i] = DemSeat;
608
617
  this.statistics[DistrictField.WhitePop][i] = whitePop;
609
618
  this.statistics[DistrictField.MinorityPop][i] = minorityPop;
@@ -621,11 +630,13 @@ class Districts {
621
630
  this.statistics[DistrictField.AsianPct][i] = asianPct;
622
631
  this.statistics[DistrictField.NativePct][i] = nativePct;
623
632
  }
624
- // 7 - MORE ...
625
633
  { // ACCUMULATE STATE STATISTICS FROM DISTRICT TOTALS
626
- stateTPVote += totVotes;
634
+ // 10-22-2020 - Add OtherVotes
635
+ stateTotVote += totVotes;
636
+ // stateTPVote += totVotes;
627
637
  stateDemVote += demVotes;
628
638
  stateRepVote += repVotes;
639
+ stateOthVote += othVotes;
629
640
  stateVAPPop += totalVAP;
630
641
  stateWhitePop += whitePop;
631
642
  stateMinorityPop += minorityPop;
@@ -638,11 +649,14 @@ class Districts {
638
649
  }
639
650
  else { // If a district is empty, zero these results (vs. null)
640
651
  this.statistics[DistrictField.PopDevPct][i] = popDevPct;
652
+ // 10-22-2020 - Add OtherVotes
641
653
  this.statistics[DistrictField.DemVotes][i] = 0;
642
654
  this.statistics[DistrictField.RepVotes][i] = 0;
643
- this.statistics[DistrictField.TwoPartyVote][i] = 0;
655
+ this.statistics[DistrictField.OtherVotes][i] = 0;
656
+ // this.statistics[DistrictField.TwoPartyVote][i] = 0;
644
657
  this.statistics[DistrictField.DemPct][i] = 0;
645
658
  this.statistics[DistrictField.RepPct][i] = 0;
659
+ this.statistics[DistrictField.OtherPct][i] = 0;
646
660
  this.statistics[DistrictField.DemSeat][i] = 0;
647
661
  this.statistics[DistrictField.WhitePop][i] = 0;
648
662
  this.statistics[DistrictField.MinorityPop][i] = 0;
@@ -671,9 +685,13 @@ class Districts {
671
685
  }
672
686
  // UPDATE STATE STATISTICS
673
687
  let summaryRow = this.numberOfRows() - 1;
674
- if (stateTPVote > 0) {
675
- this.statistics[DistrictField.DemPct][summaryRow] = stateDemVote / stateTPVote;
676
- this.statistics[DistrictField.RepPct][summaryRow] = stateRepVote / stateTPVote;
688
+ // 10-22-2020 - Add OtherVotes
689
+ if (stateTotVote > 0) {
690
+ this.statistics[DistrictField.DemVotes][summaryRow] = stateDemVote;
691
+ this.statistics[DistrictField.RepVotes][summaryRow] = stateRepVote;
692
+ this.statistics[DistrictField.DemPct][summaryRow] = stateDemVote / stateTotVote;
693
+ this.statistics[DistrictField.RepPct][summaryRow] = stateRepVote / stateTotVote;
694
+ this.statistics[DistrictField.OtherPct][summaryRow] = stateOthVote / stateTotVote;
677
695
  }
678
696
  if (stateVAPPop > 0) {
679
697
  this.statistics[DistrictField.TotalVAP][summaryRow] = stateVAPPop;
@@ -1264,7 +1282,7 @@ function scoreKIWYSICompactness(s, bLog = false) {
1264
1282
  }
1265
1283
  const scores = Compactness.scoreShapes(goodShapes);
1266
1284
  // 10-21-2020
1267
- // - Return all by - district scores instead of just the average.
1285
+ // - Return all by-district scores instead of just the average.
1268
1286
  // - Round & invert the scores here at the source vs. later on, higher up.
1269
1287
  return scores.map(n => 100 - Math.round(n));
1270
1288
  // const avgKIWYSIScore: number = U.avgArray(scores);
@@ -1913,6 +1931,7 @@ const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
1913
1931
  const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
1914
1932
  const C = __importStar(__webpack_require__(/*! ./compact */ "./src/compact.ts"));
1915
1933
  // PROFILE A PLAN
1934
+ const KEEP_DECIMALS = 6;
1916
1935
  function profilePlan(s, bLog = false) {
1917
1936
  const state = s.state.xx;
1918
1937
  const planName = s.title;
@@ -1923,8 +1942,22 @@ function profilePlan(s, bLog = false) {
1923
1942
  const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
1924
1943
  const splits = makeNakedCxD(s);
1925
1944
  const summaryRow = s.districts.numberOfRows() - 1;
1926
- const statewideVf = U.trim(s.districts.statistics[D.DistrictField.DemPct][summaryRow], 6);
1927
- const vpiArray = U.deepCopy(s.districts.statistics[D.DistrictField.DemPct].slice(1, -1));
1945
+ // 10-22-2020 - TODO: Convert Dem + Rep + Other = Total to two-party vote shares for analytics.
1946
+ const demVote = s.districts.statistics[D.DistrictField.DemVotes][summaryRow];
1947
+ const repVote = s.districts.statistics[D.DistrictField.RepVotes][summaryRow];
1948
+ const statewideVf = U.trim(demVote / (demVote + repVote), KEEP_DECIMALS);
1949
+ // const statewideVf: number = U.trim(s.districts.statistics[D.DistrictField.DemPct][summaryRow], 6);
1950
+ let vpiArray = [];
1951
+ const demVotes = U.deepCopy(s.districts.statistics[D.DistrictField.DemVotes].slice(1, -1));
1952
+ const repVotes = U.deepCopy(s.districts.statistics[D.DistrictField.RepVotes].slice(1, -1));
1953
+ for (let districtID = 1; districtID <= nDistricts; districtID++) {
1954
+ const D = demVotes[districtID - 1];
1955
+ const R = repVotes[districtID - 1];
1956
+ const T = D + R;
1957
+ const v = (T > 0) ? U.trim(D / T, KEEP_DECIMALS) : 0;
1958
+ vpiArray.push(v);
1959
+ }
1960
+ // const vpiArray: number[] = U.deepCopy(s.districts.statistics[D.DistrictField.DemPct].slice(1, -1)) as number[];
1928
1961
  const statewideDemographics = getStatewideDemographics(s);
1929
1962
  const demographicsByDistrict = getDemographicsByDistrict(s);
1930
1963
  const profile = {
@@ -1974,9 +2007,9 @@ function makeArrayOfGeoProps(s, bLog = false) {
1974
2007
  let districtProps = s.districts.getGeoProperties(districtID);
1975
2008
  // Guard against no shape and no properties
1976
2009
  if (districtProps) {
1977
- let a = districtProps[0 /* Area */];
1978
- let p = districtProps[2 /* Perimeter */];
1979
- let d = districtProps[1 /* Diameter */];
2010
+ let a = U.trim(districtProps[0 /* Area */], KEEP_DECIMALS);
2011
+ let p = U.trim(districtProps[2 /* Perimeter */], KEEP_DECIMALS);
2012
+ let d = U.trim(districtProps[1 /* Diameter */], KEEP_DECIMALS);
1980
2013
  // Save each triple
1981
2014
  geometryByDistrict.push([a, p, d]);
1982
2015
  }
@@ -1988,13 +2021,13 @@ function getDemographicsByDistrict(s, bLog = false) {
1988
2021
  // Remove the unassigned & total dummy "districts"
1989
2022
  for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
1990
2023
  const districtDemographics = [
1991
- U.deepCopy(s.districts.statistics[D.DistrictField.WhitePct][districtID]),
1992
- U.deepCopy(s.districts.statistics[D.DistrictField.MinorityPct][districtID]),
1993
- U.deepCopy(s.districts.statistics[D.DistrictField.BlackPct][districtID]),
1994
- U.deepCopy(s.districts.statistics[D.DistrictField.HispanicPct][districtID]),
1995
- U.deepCopy(s.districts.statistics[D.DistrictField.PacificPct][districtID]),
1996
- U.deepCopy(s.districts.statistics[D.DistrictField.AsianPct][districtID]),
1997
- U.deepCopy(s.districts.statistics[D.DistrictField.NativePct][districtID])
2024
+ U.trim(s.districts.statistics[D.DistrictField.WhitePct][districtID], KEEP_DECIMALS),
2025
+ U.trim(s.districts.statistics[D.DistrictField.MinorityPct][districtID], KEEP_DECIMALS),
2026
+ U.trim(s.districts.statistics[D.DistrictField.BlackPct][districtID], KEEP_DECIMALS),
2027
+ U.trim(s.districts.statistics[D.DistrictField.HispanicPct][districtID], KEEP_DECIMALS),
2028
+ U.trim(s.districts.statistics[D.DistrictField.PacificPct][districtID], KEEP_DECIMALS),
2029
+ U.trim(s.districts.statistics[D.DistrictField.AsianPct][districtID], KEEP_DECIMALS),
2030
+ U.trim(s.districts.statistics[D.DistrictField.NativePct][districtID], KEEP_DECIMALS)
1998
2031
  ];
1999
2032
  demographicsArray.push(districtDemographics);
2000
2033
  }
@@ -2003,13 +2036,13 @@ function getDemographicsByDistrict(s, bLog = false) {
2003
2036
  function getStatewideDemographics(s, bLog = false) {
2004
2037
  const summaryRow = s.districts.numberOfRows() - 1;
2005
2038
  const demographicsArray = [
2006
- U.deepCopy(s.districts.statistics[D.DistrictField.WhitePct][summaryRow]),
2007
- U.deepCopy(s.districts.statistics[D.DistrictField.MinorityPct][summaryRow]),
2008
- U.deepCopy(s.districts.statistics[D.DistrictField.BlackPct][summaryRow]),
2009
- U.deepCopy(s.districts.statistics[D.DistrictField.HispanicPct][summaryRow]),
2010
- U.deepCopy(s.districts.statistics[D.DistrictField.PacificPct][summaryRow]),
2011
- U.deepCopy(s.districts.statistics[D.DistrictField.AsianPct][summaryRow]),
2012
- U.deepCopy(s.districts.statistics[D.DistrictField.NativePct][summaryRow])
2039
+ U.trim(s.districts.statistics[D.DistrictField.WhitePct][summaryRow], KEEP_DECIMALS),
2040
+ U.trim(s.districts.statistics[D.DistrictField.MinorityPct][summaryRow], KEEP_DECIMALS),
2041
+ U.trim(s.districts.statistics[D.DistrictField.BlackPct][summaryRow], KEEP_DECIMALS),
2042
+ U.trim(s.districts.statistics[D.DistrictField.HispanicPct][summaryRow], KEEP_DECIMALS),
2043
+ U.trim(s.districts.statistics[D.DistrictField.PacificPct][summaryRow], KEEP_DECIMALS),
2044
+ U.trim(s.districts.statistics[D.DistrictField.AsianPct][summaryRow], KEEP_DECIMALS),
2045
+ U.trim(s.districts.statistics[D.DistrictField.NativePct][summaryRow], KEEP_DECIMALS)
2013
2046
  ];
2014
2047
  return demographicsArray;
2015
2048
  }
@@ -2043,8 +2076,8 @@ function scorePlan(s, p, bLog = false, overridesJSON) {
2043
2076
  const avgKIWYSIScore = Math.round(U.avgArray(kiwysiScores));
2044
2077
  scorecard.compactness.details['kiwysi'] = avgKIWYSIScore;
2045
2078
  // 10-21-2020 - Add KIWYSI scores to the by-district details
2046
- const nDistricts = s.state.nDistricts;
2047
2079
  let byDistrict = scorecard.compactness.details['byDistrict'];
2080
+ const nDistricts = byDistrict.length;
2048
2081
  for (let districtID = 1; districtID <= nDistricts; districtID++) {
2049
2082
  byDistrict[districtID - 1].kiwysiScore = kiwysiScores[districtID - 1];
2050
2083
  }