@dra2020/district-analytics 3.0.1 → 3.3.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/dist/cli.js CHANGED
@@ -89264,13 +89264,24 @@ module.exports = g;
89264
89264
  //
89265
89265
  // THE NODE PACKAGE API
89266
89266
  //
89267
+ var __importStar = (this && this.__importStar) || function (mod) {
89268
+ if (mod && mod.__esModule) return mod;
89269
+ var result = {};
89270
+ if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
89271
+ result["default"] = mod;
89272
+ return result;
89273
+ };
89267
89274
  Object.defineProperty(exports, "__esModule", { value: true });
89275
+ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
89268
89276
  const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
89269
89277
  class Scorer {
89270
89278
  constructor() {
89271
89279
  }
89272
- score(plan) {
89273
- return score_1.scorePlan(plan);
89280
+ score(profile, overridesJSON) {
89281
+ return score_1.scorePlan(profile, overridesJSON);
89282
+ }
89283
+ populationDeviationThreshold(bLegislative) {
89284
+ return C.popdevThreshold(bLegislative);
89274
89285
  }
89275
89286
  }
89276
89287
  exports.Scorer = Scorer;
@@ -89300,27 +89311,33 @@ var __importStar = (this && this.__importStar) || function (mod) {
89300
89311
  Object.defineProperty(exports, "__esModule", { value: true });
89301
89312
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
89302
89313
  const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
89314
+ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
89303
89315
  const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
89304
89316
  function scoreCountySplitting(rawValue, nCounties, nDistricts) {
89305
89317
  const _normalizer = new normalize_1.Normalizer(rawValue);
89306
- const stateCountySplitThreshold = countySplitThreshold(nCounties, nDistricts);
89307
- _normalizer.clip(S.COUNTY_BEST, stateCountySplitThreshold);
89308
- _normalizer.unitize(S.COUNTY_BEST, stateCountySplitThreshold);
89318
+ const best = C.countySplittingRange()[C.BEG];
89319
+ const worst = countySplitThreshold(nCounties, nDistricts);
89320
+ _normalizer.clip(best, worst);
89321
+ _normalizer.unitize(best, worst);
89309
89322
  _normalizer.invert();
89310
89323
  _normalizer.rescale();
89311
89324
  return _normalizer.normalizedNum;
89312
89325
  }
89313
89326
  exports.scoreCountySplitting = scoreCountySplitting;
89314
89327
  function countySplitThreshold(nCounties, nDistricts) {
89315
- const nAllowableSplits = Math.min(S.ALLOWABLE_SPLITS_MULTIPLIER * (nDistricts - 1));
89316
- const threshold = ((nAllowableSplits * S.COUNTY_WORST) + ((nCounties - nAllowableSplits) * 1.0)) / nCounties;
89328
+ const m = C.allowableSplitsMultiplier();
89329
+ const worst = C.countySplittingRange()[1];
89330
+ const nAllowableSplits = Math.min(m * (nDistricts - 1));
89331
+ const threshold = ((nAllowableSplits * worst) + ((nCounties - nAllowableSplits) * 1.0)) / nCounties;
89317
89332
  return threshold;
89318
89333
  }
89319
89334
  exports.countySplitThreshold = countySplitThreshold;
89320
89335
  function scoreDistrictSplitting(rawValue) {
89321
89336
  const _normalizer = new normalize_1.Normalizer(rawValue);
89322
- _normalizer.clip(S.DISTRICT_BEST, S.DISTRICT_WORST);
89323
- _normalizer.unitize(S.DISTRICT_BEST, S.DISTRICT_WORST);
89337
+ const best = C.districtSplittingRange()[C.BEG];
89338
+ const worst = C.districtSplittingRange()[C.END];
89339
+ _normalizer.clip(best, worst);
89340
+ _normalizer.unitize(best, worst);
89324
89341
  _normalizer.invert();
89325
89342
  _normalizer.rescale();
89326
89343
  return _normalizer.normalizedNum;
@@ -89593,7 +89610,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
89593
89610
  };
89594
89611
  Object.defineProperty(exports, "__esModule", { value: true });
89595
89612
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
89596
- const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
89613
+ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
89597
89614
  const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
89598
89615
  // Migrated from district-analytics:
89599
89616
  // Measures of compactness compare district shapes to various ideally compact
@@ -89715,8 +89732,10 @@ function calcReock(area, diameter) {
89715
89732
  exports.calcReock = calcReock;
89716
89733
  function scoreReock(rawValue) {
89717
89734
  const _normalizer = new normalize_1.Normalizer(rawValue);
89718
- _normalizer.clip(S.REOCK_WORST, S.REOCK_BEST);
89719
- _normalizer.unitize(S.REOCK_WORST, S.REOCK_BEST);
89735
+ const worst = C.reockRange()[C.BEG];
89736
+ const best = C.reockRange()[C.END];
89737
+ _normalizer.clip(worst, best);
89738
+ _normalizer.unitize(worst, best);
89720
89739
  _normalizer.rescale();
89721
89740
  return _normalizer.normalizedNum;
89722
89741
  }
@@ -89734,12 +89753,12 @@ function doPolsbyPopper(g, bLog = false) {
89734
89753
  if (districtProps) {
89735
89754
  let a = districtProps[0 /* Area */];
89736
89755
  let p = districtProps[1 /* Perimeter */];
89737
- let polsbyPopper = calcPolsbyPopper(a, p);
89756
+ let polsby = calcPolsbyPopper(a, p);
89738
89757
  // Save each district score
89739
- scores.push(polsbyPopper);
89758
+ scores.push(polsby);
89740
89759
  // Echo the results by district
89741
89760
  if (bLog)
89742
- console.log("Polsby-Popper for district", districtID, "=", polsbyPopper);
89761
+ console.log("Polsby-Popper for district", districtID, "=", polsby);
89743
89762
  }
89744
89763
  else {
89745
89764
  console.log("No shape or no geopropertes in Polsby-Popper ...");
@@ -89762,14 +89781,202 @@ function calcPolsbyPopper(area, perimeter) {
89762
89781
  exports.calcPolsbyPopper = calcPolsbyPopper;
89763
89782
  function scorePolsbyPopper(rawValue) {
89764
89783
  const _normalizer = new normalize_1.Normalizer(rawValue);
89765
- _normalizer.clip(S.POLSBY_WORST, S.POLSBY_BEST);
89766
- _normalizer.unitize(S.POLSBY_WORST, S.POLSBY_BEST);
89784
+ const worst = C.polsbyRange()[C.BEG];
89785
+ const best = C.polsbyRange()[C.END];
89786
+ _normalizer.clip(worst, best);
89787
+ _normalizer.unitize(worst, best);
89767
89788
  _normalizer.rescale();
89768
89789
  return _normalizer.normalizedNum;
89769
89790
  }
89770
89791
  exports.scorePolsbyPopper = scorePolsbyPopper;
89771
89792
 
89772
89793
 
89794
+ /***/ }),
89795
+
89796
+ /***/ "./src/config.json":
89797
+ /*!*************************!*\
89798
+ !*** ./src/config.json ***!
89799
+ \*************************/
89800
+ /*! exports provided: partisan, traditionalPrinciples, default */
89801
+ /***/ (function(module) {
89802
+
89803
+ module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"overall\":{\"range\":[0,0.67],\"weight\":25},\"marginal\":{\"range\":[0,0.85],\"weight\":75},\"range\":[0.45,0.55],\"distribution\":[0.25,0.75],\"weight\":[0,20]},\"weight\":[100,80]},\"traditionalPrinciples\":{\"compactness\":{\"reock\":{\"range\":[0.25,0.5],\"weight\":50},\"polsby\":{\"range\":[0.1,0.5],\"weight\":50},\"weight\":[0,50]},\"splitting\":{\"county\":{\"range\":[1,1.71],\"allowableSplitsMultiplier\":1.5,\"weight\":50},\"district\":{\"range\":[1,1.5],\"weight\":50},\"weight\":[0,50]},\"popdev\":{\"range\":[[0.0075,0.002],[0.1,-1]],\"weight\":[0,0]},\"weight\":[0,20]}}");
89804
+
89805
+ /***/ }),
89806
+
89807
+ /***/ "./src/config.ts":
89808
+ /*!***********************!*\
89809
+ !*** ./src/config.ts ***!
89810
+ \***********************/
89811
+ /*! no static exports found */
89812
+ /***/ (function(module, exports, __webpack_require__) {
89813
+
89814
+ "use strict";
89815
+
89816
+ //
89817
+ // THRESHOLDS, SCALES, AND WEIGHTS FOR SCORING MODEL
89818
+ //
89819
+ // * A layer of functions over config.json, so that they can be overridden dynamically (TODO)
89820
+ //
89821
+ var __importDefault = (this && this.__importDefault) || function (mod) {
89822
+ return (mod && mod.__esModule) ? mod : { "default": mod };
89823
+ };
89824
+ Object.defineProperty(exports, "__esModule", { value: true });
89825
+ // Defaults
89826
+ const config_json_1 = __importDefault(__webpack_require__(/*! ./config.json */ "./src/config.json"));
89827
+ exports.BEG = 0;
89828
+ exports.END = 1;
89829
+ // PARTISAN
89830
+ function biasWeight(context, overridesJSON) {
89831
+ const bW = config_json_1.default.partisan.bias.weight[context];
89832
+ return bW;
89833
+ }
89834
+ exports.biasWeight = biasWeight;
89835
+ function biasRange(overridesJSON) {
89836
+ const range = config_json_1.default.partisan.bias.range;
89837
+ return range;
89838
+ }
89839
+ exports.biasRange = biasRange;
89840
+ function impactWeight(context, overridesJSON) {
89841
+ const iW = config_json_1.default.partisan.impact.weight[context];
89842
+ return iW;
89843
+ }
89844
+ exports.impactWeight = impactWeight;
89845
+ // The maximum # of unearned seats that scores positively
89846
+ function unearnedThreshold(overridesJSON) {
89847
+ const threshold = config_json_1.default.partisan.impact.threshold;
89848
+ return threshold;
89849
+ }
89850
+ exports.unearnedThreshold = unearnedThreshold;
89851
+ function competitivenessWeight(context, overridesJSON) {
89852
+ const cW = config_json_1.default.partisan.competitiveness.weight[context];
89853
+ return cW;
89854
+ }
89855
+ exports.competitivenessWeight = competitivenessWeight;
89856
+ function competitiveRange(overridesJSON) {
89857
+ const range = config_json_1.default.partisan.competitiveness.range;
89858
+ return range;
89859
+ }
89860
+ exports.competitiveRange = competitiveRange;
89861
+ function competitiveDistribution(overridesJSON) {
89862
+ const dist = config_json_1.default.partisan.competitiveness.distribution;
89863
+ return dist;
89864
+ }
89865
+ exports.competitiveDistribution = competitiveDistribution;
89866
+ function overallCompetitivenessRange(overridesJSON) {
89867
+ const range = config_json_1.default.partisan.competitiveness.overall.range;
89868
+ return range;
89869
+ }
89870
+ exports.overallCompetitivenessRange = overallCompetitivenessRange;
89871
+ function marginalCompetitivenessRange(overridesJSON) {
89872
+ const range = config_json_1.default.partisan.competitiveness.marginal.range;
89873
+ return range;
89874
+ }
89875
+ exports.marginalCompetitivenessRange = marginalCompetitivenessRange;
89876
+ function marginalCompetitivenessWeight(overridesJSON) {
89877
+ const mcW = config_json_1.default.partisan.competitiveness.marginal.weight;
89878
+ return mcW;
89879
+ }
89880
+ exports.marginalCompetitivenessWeight = marginalCompetitivenessWeight;
89881
+ function overallCompetitivenessWeight(overridesJSON) {
89882
+ const ocW = config_json_1.default.partisan.competitiveness.overall.weight;
89883
+ return ocW;
89884
+ }
89885
+ exports.overallCompetitivenessWeight = overallCompetitivenessWeight;
89886
+ // TRADITIONAL DISTRICTING PRINCIPLES
89887
+ function compactnessWeight(context, overridesJSON) {
89888
+ const cW = config_json_1.default.traditionalPrinciples.compactness.weight[context];
89889
+ return cW;
89890
+ }
89891
+ exports.compactnessWeight = compactnessWeight;
89892
+ function reockWeight(overridesJSON) {
89893
+ const rW = config_json_1.default.traditionalPrinciples.compactness.reock.weight;
89894
+ return rW;
89895
+ }
89896
+ exports.reockWeight = reockWeight;
89897
+ function reockRange(overridesJSON) {
89898
+ const range = config_json_1.default.traditionalPrinciples.compactness.reock.range;
89899
+ return range;
89900
+ }
89901
+ exports.reockRange = reockRange;
89902
+ function polsbyWeight(overridesJSON) {
89903
+ const ppW = config_json_1.default.traditionalPrinciples.compactness.polsby.weight;
89904
+ return ppW;
89905
+ }
89906
+ exports.polsbyWeight = polsbyWeight;
89907
+ function polsbyRange(overridesJSON) {
89908
+ const range = config_json_1.default.traditionalPrinciples.compactness.polsby.range;
89909
+ return range;
89910
+ }
89911
+ exports.polsbyRange = polsbyRange;
89912
+ function splittingWeight(context, overridesJSON) {
89913
+ const sW = config_json_1.default.traditionalPrinciples.splitting.weight[context];
89914
+ return sW;
89915
+ }
89916
+ exports.splittingWeight = splittingWeight;
89917
+ function countySplittingWeight(overridesJSON) {
89918
+ const csW = config_json_1.default.traditionalPrinciples.splitting.county.weight;
89919
+ return csW;
89920
+ }
89921
+ exports.countySplittingWeight = countySplittingWeight;
89922
+ function countySplittingRange(overridesJSON) {
89923
+ const range = config_json_1.default.traditionalPrinciples.splitting.county.range;
89924
+ return range;
89925
+ }
89926
+ exports.countySplittingRange = countySplittingRange;
89927
+ function allowableSplitsMultiplier(overridesJSON) {
89928
+ const m = config_json_1.default.traditionalPrinciples.splitting.county.allowableSplitsMultiplier;
89929
+ return m;
89930
+ }
89931
+ exports.allowableSplitsMultiplier = allowableSplitsMultiplier;
89932
+ function districtSplittingWeight(overridesJSON) {
89933
+ const dsW = config_json_1.default.traditionalPrinciples.splitting.district.weight;
89934
+ return dsW;
89935
+ }
89936
+ exports.districtSplittingWeight = districtSplittingWeight;
89937
+ function districtSplittingRange(overridesJSON) {
89938
+ const range = config_json_1.default.traditionalPrinciples.splitting.district.range;
89939
+ return range;
89940
+ }
89941
+ exports.districtSplittingRange = districtSplittingRange;
89942
+ function popdevWeight(context, overridesJSON) {
89943
+ const pdW = config_json_1.default.traditionalPrinciples.popdev.weight[context];
89944
+ return pdW;
89945
+ }
89946
+ exports.popdevWeight = popdevWeight;
89947
+ // NOTE - Raw ranges, not inverted (i.e., smaller is better)
89948
+ // NOTE - This could be optimized to not calc LD values for CD's (or do it once)
89949
+ function popdevRange(bLegislative, overridesJSON) {
89950
+ const cdRange = config_json_1.default.traditionalPrinciples.popdev.range[0 /* Congressional */];
89951
+ const worstCD = cdRange[exports.BEG];
89952
+ const bestCD = cdRange[exports.END];
89953
+ const ldRange = config_json_1.default.traditionalPrinciples.popdev.range[1 /* StateLegislative */];
89954
+ const iRange = bLegislative ? ldRange : cdRange;
89955
+ const worst = iRange[exports.BEG];
89956
+ const best = bLegislative ? (bestCD / worstCD) * iRange[exports.BEG] : iRange[exports.END];
89957
+ // Invert the range, so bigger is better.
89958
+ return [worst, best];
89959
+ }
89960
+ exports.popdevRange = popdevRange;
89961
+ // NOTE - Raw threshold, not inverted (i.e., smaller is better)
89962
+ function popdevThreshold(bLegislative, overridesJSON) {
89963
+ const threshold = popdevRange(bLegislative)[exports.BEG];
89964
+ return threshold;
89965
+ }
89966
+ exports.popdevThreshold = popdevThreshold;
89967
+ // OVERALL SCORE
89968
+ function partisanWeight(context, overridesJSON) {
89969
+ const pW = config_json_1.default.partisan.weight[context];
89970
+ return pW;
89971
+ }
89972
+ exports.partisanWeight = partisanWeight;
89973
+ function traditionalPrinciplesWeight(context, overridesJSON) {
89974
+ const tpW = config_json_1.default.traditionalPrinciples.weight[context];
89975
+ return tpW;
89976
+ }
89977
+ exports.traditionalPrinciplesWeight = traditionalPrinciplesWeight;
89978
+
89979
+
89773
89980
  /***/ }),
89774
89981
 
89775
89982
  /***/ "./src/equal.ts":
@@ -89793,7 +90000,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
89793
90000
  };
89794
90001
  Object.defineProperty(exports, "__esModule", { value: true });
89795
90002
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
89796
- const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
90003
+ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
89797
90004
  const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
89798
90005
  // Migrated from district-analytics
89799
90006
  // Expect a full dict of district IDs & values (which might be zero = empty)
@@ -89814,7 +90021,11 @@ function doPopulationDeviation(e, targetSize, bLegislative, bLog = false) {
89814
90021
  // Round the raw value to the desired level of precision
89815
90022
  const popDev = U.trim((max - min) / targetSize);
89816
90023
  const score = scorePopulationDeviation(popDev, bLegislative);
89817
- const notes = { 'maxDeviation': max - min };
90024
+ const threshold = C.popdevThreshold(bLegislative);
90025
+ const notes = {
90026
+ 'maxDeviation': max - min,
90027
+ 'threshold': threshold
90028
+ };
89818
90029
  // Populate the measurement
89819
90030
  const m = {
89820
90031
  raw: popDev,
@@ -89826,11 +90037,11 @@ function doPopulationDeviation(e, targetSize, bLegislative, bLog = false) {
89826
90037
  exports.doPopulationDeviation = doPopulationDeviation;
89827
90038
  function scorePopulationDeviation(rawValue, bLegislative) {
89828
90039
  const _normalizer = new normalize_1.Normalizer(rawValue);
89829
- const rangeMin = bLegislative ? S.POPEQ_MIN_LD : S.POPEQ_MIN;
89830
- const rangeMax = bLegislative ? S.POPEQ_MAX_LD : S.POPEQ_MAX;
90040
+ // Raw range in not inverted (i.e., smaller is better)
90041
+ const range = C.popdevRange(bLegislative);
89831
90042
  _normalizer.invert();
89832
- _normalizer.clip(rangeMin, rangeMax);
89833
- _normalizer.unitize(rangeMin, rangeMax);
90043
+ _normalizer.clip(1.0 - range[C.BEG], 1.0 - range[C.END]);
90044
+ _normalizer.unitize(1.0 - range[C.BEG], 1.0 - range[C.END]);
89834
90045
  _normalizer.rescale();
89835
90046
  return _normalizer.normalizedNum;
89836
90047
  }
@@ -89839,10 +90050,129 @@ exports.scorePopulationDeviation = scorePopulationDeviation;
89839
90050
 
89840
90051
  /***/ }),
89841
90052
 
89842
- /***/ "./src/fair.ts":
89843
- /*!*********************!*\
89844
- !*** ./src/fair.ts ***!
89845
- \*********************/
90053
+ /***/ "./src/index.ts":
90054
+ /*!**********************!*\
90055
+ !*** ./src/index.ts ***!
90056
+ \**********************/
90057
+ /*! no static exports found */
90058
+ /***/ (function(module, exports, __webpack_require__) {
90059
+
90060
+ "use strict";
90061
+
90062
+ //
90063
+ // SCORE PACKAGE API
90064
+ //
90065
+ function __export(m) {
90066
+ for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
90067
+ }
90068
+ Object.defineProperty(exports, "__esModule", { value: true });
90069
+ __export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
90070
+ var types_1 = __webpack_require__(/*! ./types */ "./src/types.ts");
90071
+ exports.sampleProfile = types_1.sampleProfile;
90072
+ exports.sampleScorecard = types_1.sampleScorecard;
90073
+
90074
+
90075
+ /***/ }),
90076
+
90077
+ /***/ "./src/normalize.ts":
90078
+ /*!**************************!*\
90079
+ !*** ./src/normalize.ts ***!
90080
+ \**************************/
90081
+ /*! no static exports found */
90082
+ /***/ (function(module, exports, __webpack_require__) {
90083
+
90084
+ "use strict";
90085
+
90086
+ //
90087
+ // NORMALIZATION UTILITIES
90088
+ //
90089
+ var __importStar = (this && this.__importStar) || function (mod) {
90090
+ if (mod && mod.__esModule) return mod;
90091
+ var result = {};
90092
+ if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
90093
+ result["default"] = mod;
90094
+ return result;
90095
+ };
90096
+ Object.defineProperty(exports, "__esModule", { value: true });
90097
+ const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
90098
+ class Normalizer {
90099
+ constructor(rawValue) {
90100
+ this.rawNum = rawValue;
90101
+ this.wipNum = this.rawNum;
90102
+ }
90103
+ // *Don't* transform the input.
90104
+ identity() {
90105
+ return this.wipNum;
90106
+ }
90107
+ positive() {
90108
+ this.wipNum = Math.abs(this.wipNum);
90109
+ return this.wipNum;
90110
+ }
90111
+ // Invert a value in the unit range [0.0–1.0] (so that bigger is better).
90112
+ invert() {
90113
+ // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90114
+ console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Inverting: " + S.OUT_OF_RANGE_MSG, this.wipNum);
90115
+ this.wipNum = 1.0 - this.wipNum;
90116
+ return this.wipNum;
90117
+ }
90118
+ // Constrain the value to be within a specified range.
90119
+ clip(begin_range, end_range) {
90120
+ // Handle the ends of the range being given either order
90121
+ const min_range = Math.min(begin_range, end_range);
90122
+ const max_range = Math.max(begin_range, end_range);
90123
+ this.wipNum = Math.max(Math.min(this.wipNum, max_range), min_range);
90124
+ return this.wipNum;
90125
+ }
90126
+ // Recast the value as the delta from a baseline value. NOOP if it is zero.
90127
+ // NOTE - Values can be + or -.
90128
+ rebase(base) {
90129
+ this.wipNum = this.wipNum - base;
90130
+ return this.wipNum;
90131
+ }
90132
+ // Re-scale a value into the [0.0 – 1.0] range, using a given range.
90133
+ // NOTE - This assumes that values have alrady been clipped into the range.
90134
+ unitize(begin_range, end_range) {
90135
+ const min_range = Math.min(begin_range, end_range);
90136
+ const max_range = Math.max(begin_range, end_range);
90137
+ // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90138
+ console.assert(((this.wipNum >= min_range) && (this.wipNum <= max_range)), "Unitizing: " + S.OUT_OF_RANGE_MSG, this.wipNum);
90139
+ const ranged = this.wipNum - min_range;
90140
+ this.wipNum = Math.abs(ranged / (end_range - begin_range));
90141
+ return this.wipNum;
90142
+ }
90143
+ // Decay a value in the unit range [0.0–1.0] by its distance from zero.
90144
+ // NOTE - If the range is already such that "bigger is better," then the closer
90145
+ // the value is to 1.0 (the best) the *less* it will decay.
90146
+ decay(fn = 0 /* Gravity */) {
90147
+ // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90148
+ console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Decaying: " + S.OUT_OF_RANGE_MSG, this.wipNum);
90149
+ switch (fn) {
90150
+ case 0 /* Gravity */: {
90151
+ this.wipNum = Math.pow(this.wipNum, S.DISTANCE_WEIGHT);
90152
+ return this.wipNum;
90153
+ }
90154
+ default: {
90155
+ throw new Error("Decay function not recognized.");
90156
+ }
90157
+ }
90158
+ }
90159
+ // Translate a value in the unit range to the user-friendly range [0 – 100].
90160
+ rescale() {
90161
+ // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90162
+ console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), "Rescaling: " + S.OUT_OF_RANGE_MSG, this.wipNum);
90163
+ this.normalizedNum = Math.round(this.wipNum * S.NORMALIZED_RANGE);
90164
+ return this.normalizedNum;
90165
+ }
90166
+ }
90167
+ exports.Normalizer = Normalizer;
90168
+
90169
+
90170
+ /***/ }),
90171
+
90172
+ /***/ "./src/partisan.ts":
90173
+ /*!*************************!*\
90174
+ !*** ./src/partisan.ts ***!
90175
+ \*************************/
89846
90176
  /*! no static exports found */
89847
90177
  /***/ (function(module, exports, __webpack_require__) {
89848
90178
 
@@ -89861,48 +90191,61 @@ var __importStar = (this && this.__importStar) || function (mod) {
89861
90191
  Object.defineProperty(exports, "__esModule", { value: true });
89862
90192
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
89863
90193
  const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
90194
+ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
89864
90195
  const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts");
89865
90196
  // NOTE - I'm passing T.VfArray's into everything. District indices = array indices.
89866
90197
  // NOTE - I do not (cannot) assume that the values are sorted.
89867
90198
  // SCORE BIAS & COMPETITIVENESS
89868
- /* SCORECARD FIELDS:
90199
+ /* SCORECARD FIELDS <<< TODO: Update
89869
90200
 
89870
90201
  * ^S# [BestS] = the Democratic seats closest to proportional
89871
90202
  * ^S% [BestSf] = the corresponding Democratic seat share
89872
- * F# [FptpS] = the estimated number of Democratic seats using first past the post
90203
+ * S! [FptpS] = the estimated number of Democratic seats using first past the post
89873
90204
  * S# [ProbableS] = the estimated Democratic seats, using seat probabilities
89874
90205
  * S% [ProbableSf] = the estimated Democratic seat share fraction, calculated as S# / N
90206
+ * UE# [UnearnedS] = the number of unearned seats (R = positive; D = negative)
89875
90207
  * B% [Bias]= the bias calculated as S% – ^S%
89876
90208
  * B$ [BiasScore] = the bias score normalized [0–100]
89877
90209
 
89878
90210
  * R# [R] = the responsiveness at V%
89879
90211
  * Rd [Rd] = the estimated # of responsive districts (using probabilities)
89880
90212
  * Rd% [Rdf] = the estimated # of responsive districts, as a fraction of N
90213
+
90214
+ * C [C] = the number of districts that fall into the range [45–55%]
89881
90215
  * Cd [Cd] = the estimated # of competitive districts, using probabilities & a narrower [0.25–0.75] range
89882
90216
  * Cd% [Cdf] = the estimated # of competitive districts, as a fraction of N
90217
+ * [beg, end] [Mrange] = the 1–N indices for the first and last district that separate the likely result and the proportional result
90218
+ * Md [Md] = the competitiveness of the marginal districts
89883
90219
  * Md% [Mdf] = the probability that the marginal seats will flip, so S% => ^S%
89884
90220
  * C$ [CompetitiveScore] = the competitiveness score normalized [0–100]
89885
90221
 
89886
- * $$$ = the combined partisan score normalize [0–100]
90222
+ * F$ = the combined partisan score normalize [0–100]
89887
90223
 
89888
90224
  */
89889
- function scorePartisan(Vf, VfArray, all = false) {
90225
+ function scorePartisan(Vf, VfArray, bAll = false, bConstrained = true) {
89890
90226
  const N = VfArray.length;
89891
90227
  const BestS = bestSeats(N, Vf);
89892
90228
  const BestSf = bestSeatShare(BestS, N);
89893
- const FptpS = all ? estFPTPSeats(VfArray) : undefined;
89894
- const ProbableS = estProbableSeats(VfArray);
90229
+ const FptpS = bAll ? estFPTPSeats(VfArray) : undefined;
90230
+ const range = bConstrained ? C.competitiveDistribution() : undefined;
90231
+ const ProbableS = estProbableSeats(VfArray, range);
89895
90232
  const ProbableSf = estProbableSeatShare(ProbableS, N);
89896
90233
  const Bias = estBias(ProbableSf, BestSf);
89897
- const normalizedBias = scoreBias(Bias, Vf, ProbableSf, N);
89898
- const inferredSVpoints = all ? inferSVpoints(Vf, VfArray) : undefined;
89899
- const R = all ? estResponsiveness(Vf, inferredSVpoints) : undefined;
89900
- const Rd = all ? estResponsiveDistricts(VfArray) : undefined;
89901
- const Rdf = all ? estResponsiveDistrictsShare(Rd, N) : undefined;
89902
- const Cd = estCompetitiveDistricts(VfArray);
90234
+ const biasScore = scoreBias(Bias, Vf, ProbableSf, N);
90235
+ const UnearnedS = estUnearnedSeats(BestS, ProbableS);
90236
+ const impactScore = scoreImpact(UnearnedS);
90237
+ const bInferSV = (bAll || (!bConstrained));
90238
+ const inferredSVpoints = bInferSV ? inferSVpoints(Vf, VfArray, range) : undefined;
90239
+ const R = bInferSV ? estResponsiveness(Vf, inferredSVpoints) : undefined;
90240
+ const Rd = bInferSV ? estResponsiveDistricts(VfArray) : undefined;
90241
+ const Rdf = bInferSV ? estResponsiveDistrictsShare(Rd, N) : undefined;
90242
+ const Cn = countCompetitiveDistricts(VfArray);
90243
+ const Cd = bConstrained ? estCompetitiveDistricts(VfArray) : Rd;
89903
90244
  const Cdf = estCompetitiveDistrictsShare(Cd, N);
89904
- const Mdf = estMarginalCompetitiveShare(Vf, VfArray, N);
89905
- const normalizedCompetitiveness = scoreCompetitiveness(Mdf, Cdf);
90245
+ const Mrange = findMarginalDistricts(Vf, VfArray, N);
90246
+ const Md = estMarginalCompetitiveDistricts(Mrange, VfArray);
90247
+ const Mdf = estMarginalCompetitiveShare(Md, Mrange);
90248
+ const competitivenessScore = scoreCompetitiveness(Mdf, Cdf);
89906
90249
  const biasScoring = {
89907
90250
  BestS: BestS,
89908
90251
  BestSf: BestSf,
@@ -89910,61 +90253,73 @@ function scorePartisan(Vf, VfArray, all = false) {
89910
90253
  ProbableS: ProbableS,
89911
90254
  ProbableSf: ProbableSf,
89912
90255
  Bias: Bias,
89913
- normalized: normalizedBias
90256
+ score: biasScore
89914
90257
  };
89915
- let competitiveScoring;
89916
- if (all) {
89917
- competitiveScoring = {
89918
- R: R,
89919
- Rd: Rd,
89920
- Rdf: Rdf,
89921
- Cd: Cd,
89922
- Cdf: Cdf,
89923
- Mdf: Mdf,
89924
- normalized: normalizedCompetitiveness
89925
- };
89926
- }
89927
- else {
89928
- competitiveScoring = {
89929
- Cd: Cd,
89930
- Cdf: Cdf,
89931
- Mdf: Mdf,
89932
- normalized: normalizedCompetitiveness
89933
- };
89934
- }
90258
+ const impactScoring = {
90259
+ UnearnedS: UnearnedS,
90260
+ score: impactScore
90261
+ };
90262
+ let competitiveScoring = {
90263
+ R: R,
90264
+ Rd: Rd,
90265
+ Rdf: Rdf,
90266
+ C: Cn,
90267
+ Cd: Cd,
90268
+ Cdf: Cdf,
90269
+ Mrange: Mrange,
90270
+ Md: Md,
90271
+ Mdf: Mdf,
90272
+ score: competitivenessScore
90273
+ };
90274
+ if (bAll) {
90275
+ competitiveScoring.R = R;
90276
+ competitiveScoring.Rd = Rd;
90277
+ competitiveScoring.Rdf = Rdf;
90278
+ }
90279
+ // Weight bias, impact, and competitiveness into partisan scores to compare
90280
+ // plans across states and within a state.
90281
+ const bS = biasScoring.score;
90282
+ const iS = impactScoring.score;
90283
+ const cS = competitiveScoring.score;
90284
+ const acrossStatesPartisanScore = weightPartisan(bS, iS, cS, 0 /* AcrossStates */);
90285
+ const withinStatesPartisanScore = weightPartisan(bS, iS, cS, 1 /* WithinAState */);
89935
90286
  const s = {
89936
90287
  bias: biasScoring,
89937
- competitiveness: competitiveScoring
90288
+ impact: impactScoring,
90289
+ competitiveness: competitiveScoring,
90290
+ score: acrossStatesPartisanScore,
90291
+ score2: withinStatesPartisanScore
89938
90292
  };
89939
90293
  return s;
89940
90294
  }
89941
90295
  exports.scorePartisan = scorePartisan;
89942
- function weightPartisan(b, c) {
89943
- return Math.round(((S.BIAS_WEIGHT * b) + (S.COMPETITIVE_WEIGHT * c)) / (S.BIAS_WEIGHT + S.COMPETITIVE_WEIGHT));
90296
+ function weightPartisan(bS, iS, cS, context) {
90297
+ const bW = C.biasWeight(context);
90298
+ const iW = C.impactWeight(context);
90299
+ const cW = C.competitivenessWeight(context);
90300
+ const score = Math.round(((bS * bW) + (iS * iW) + (cS * cW)) / (bW + iW + cW));
90301
+ return score;
89944
90302
  }
89945
90303
  exports.weightPartisan = weightPartisan;
89946
- function printFairScorecardHeader() {
89947
- console.log('XX, Name, N, V%, ^S#, ^S%, F#, S#, S%, B%, B$, R#, Rd, Rd%, Cd, Cd%, Md%, C$, $$$');
90304
+ function printPartisanScorecardHeader() {
90305
+ console.log('XX, Name, N, 1/N%, V%, ^S#, ^S%, S!, S#, S%, B%, B$, UE#, I$, R#, Rd, Rd%, C#, Cd, Cd%, beg, end, Md, Md%, C$, <P$, >P$');
89948
90306
  }
89949
- exports.printFairScorecardHeader = printFairScorecardHeader;
89950
- function printFairScorecard(xx, name, N, Vf, s) {
89951
- console.log('%s, %s, %i, %f, %i, %f, %i, %f, %f, %f, %i, %f, %f, %f, %f, %f, %f, %f, %i', xx, name, N, Vf, s.bias.BestS, s.bias.BestSf, s.bias.FptpS, s.bias.ProbableS, s.bias.ProbableSf, s.bias.Bias, s.bias.normalized, s.competitiveness.R, s.competitiveness.Rd, s.competitiveness.Rdf, s.competitiveness.Cd, s.competitiveness.Cdf, s.competitiveness.Mdf, s.competitiveness.normalized, s.score);
90307
+ exports.printPartisanScorecardHeader = printPartisanScorecardHeader;
90308
+ function printPartisanScorecardRow(xx, name, N, Vf, s) {
90309
+ console.log('%s, %s, %i, %f, %f, %i, %f, %i, %f, %f, %f, %i, %f, %i, %f, %f, %f, %i, %f, %f, %i, %i, %f, %f, %i, %i, %i', xx, name, N, U.trim(1 / N), Vf, s.bias.BestS, s.bias.BestSf, s.bias.FptpS, s.bias.ProbableS, s.bias.ProbableSf, s.bias.Bias, s.bias.score, s.impact.UnearnedS, s.impact.score, s.competitiveness.R, s.competitiveness.Rd, s.competitiveness.Rdf, s.competitiveness.C, s.competitiveness.Cd, s.competitiveness.Cdf, s.competitiveness.Mrange[C.BEG], s.competitiveness.Mrange[C.END], s.competitiveness.Md, s.competitiveness.Mdf, s.competitiveness.score, s.score, s.score2);
89952
90310
  }
89953
- exports.printFairScorecard = printFairScorecard;
90311
+ exports.printPartisanScorecardRow = printPartisanScorecardRow;
89954
90312
  function scoreBias(rawBias, Vf, Sf, N) {
89955
90313
  if (isAntimajoritarian(Vf, Sf)) {
89956
90314
  return 0;
89957
90315
  }
89958
90316
  else {
89959
90317
  const _normalizer = new normalize_1.Normalizer(rawBias);
89960
- const oneSeatFraction = 1 / N;
89961
- const worst = Math.min(oneSeatFraction, S.BIAS_WORST);
89962
- const best = S.BIAS_BEST;
90318
+ const worst = C.biasRange()[C.BEG];
90319
+ const best = C.biasRange()[C.END];
89963
90320
  _normalizer.clip(worst, best);
89964
90321
  _normalizer.unitize(worst, best);
89965
90322
  _normalizer.invert();
89966
- // TODO - SCORE: Would also decaying raw values (e.g., by squaring them) be too
89967
- // much with a small range (like 10% or one seat)?
89968
90323
  // _normalizer.decay();
89969
90324
  _normalizer.rescale();
89970
90325
  const score = _normalizer.normalizedNum;
@@ -89972,6 +90327,20 @@ function scoreBias(rawBias, Vf, Sf, N) {
89972
90327
  }
89973
90328
  }
89974
90329
  exports.scoreBias = scoreBias;
90330
+ // Normalize unearned seats
90331
+ function scoreImpact(rawUE) {
90332
+ const _normalizer = new normalize_1.Normalizer(rawUE);
90333
+ const worst = C.unearnedThreshold();
90334
+ const best = 0.0;
90335
+ _normalizer.positive();
90336
+ _normalizer.clip(worst, best);
90337
+ _normalizer.unitize(worst, best);
90338
+ _normalizer.invert();
90339
+ _normalizer.rescale();
90340
+ const score = _normalizer.normalizedNum;
90341
+ return score;
90342
+ }
90343
+ exports.scoreImpact = scoreImpact;
89975
90344
  function isAntimajoritarian(Vf, Sf) {
89976
90345
  const bDem = ((Vf < 0.5) && (Sf > 0.5)) ? true : false;
89977
90346
  const bRep = (((1 - Vf) < 0.5) && ((1 - Sf) > 0.5)) ? true : false;
@@ -89983,16 +90352,24 @@ function scoreCompetitiveness(rawMarginal, rawOverall) {
89983
90352
  // But the practical max is more like 2/3's, so unitize that range to [0.0–1.0].
89984
90353
  // Then scale the values to [0–100].
89985
90354
  const _overall = new normalize_1.Normalizer(rawOverall);
89986
- _overall.clip(S.COMPETITIVE_WORST, S.COMPETITIVE_BEST);
89987
- _overall.unitize(S.COMPETITIVE_WORST, S.COMPETITIVE_BEST);
90355
+ let worst = C.overallCompetitivenessRange()[C.BEG];
90356
+ let best = C.overallCompetitivenessRange()[C.END];
90357
+ _overall.clip(worst, best);
90358
+ _overall.unitize(worst, best);
89988
90359
  _overall.rescale();
89989
- const overall = _overall.normalizedNum;
90360
+ const ocS = _overall.normalizedNum;
89990
90361
  // Normalize marginal competitiveness
89991
90362
  const _marginal = new normalize_1.Normalizer(rawMarginal);
90363
+ worst = C.marginalCompetitivenessRange()[C.BEG];
90364
+ best = C.marginalCompetitivenessRange()[C.END];
90365
+ _marginal.clip(worst, best);
90366
+ _marginal.unitize(worst, best);
89992
90367
  _marginal.rescale();
89993
- const marginal = _marginal.normalizedNum;
90368
+ const mcS = _marginal.normalizedNum;
90369
+ const mcW = C.marginalCompetitivenessWeight();
90370
+ const ocW = C.overallCompetitivenessWeight();
89994
90371
  // Then combine the results
89995
- const score = Math.round(((S.MARGINAL_WEIGHT * marginal) + (S.OVERALL_WEIGHT * overall)) / (S.MARGINAL_WEIGHT + S.OVERALL_WEIGHT));
90372
+ const score = Math.round(((mcW * mcS) + (ocW * ocS)) / (mcW + ocW));
89996
90373
  return score;
89997
90374
  }
89998
90375
  exports.scoreCompetitiveness = scoreCompetitiveness;
@@ -90002,23 +90379,40 @@ const { erf } = __webpack_require__(/*! mathjs */ "./node_modules/mathjs/main/es
90002
90379
  // console.log("erf(-0.5) =", erf(-0.5)); // returns -0.5204998778130465
90003
90380
  // console.log("erf(4) =", erf(4)); // returns 0.9999999845827421
90004
90381
  // Estimate the probability of a seat win for district, given a Vf
90005
- function estSeatProbability(Vf) {
90382
+ function estSeatProbability(Vf, range) {
90383
+ if (range) {
90384
+ const _normalizer = new normalize_1.Normalizer(Vf);
90385
+ // The end points of a compressed probability distribution
90386
+ // NOTE - These aren't the points where races start or stop being contested,
90387
+ // just the end points of a distribution that yields the desired behavior
90388
+ // in the typical competitive range [45-55%].
90389
+ const distBeg = range[C.BEG];
90390
+ const distEnd = range[C.END];
90391
+ _normalizer.clip(distBeg, distEnd);
90392
+ _normalizer.unitize(distBeg, distEnd);
90393
+ return seatProbabilityFn(_normalizer.wipNum);
90394
+ }
90395
+ else {
90396
+ return seatProbabilityFn(Vf);
90397
+ }
90398
+ }
90399
+ exports.estSeatProbability = estSeatProbability;
90400
+ function seatProbabilityFn(Vf) {
90006
90401
  // Python: 0.5 * (1 + erf((vpi - 0.50) / (0.02 * sqrt(8))))
90007
90402
  return U.trim(0.5 * (1.0 + erf((Vf - 0.50) / (0.02 * Math.sqrt(8)))));
90008
90403
  }
90009
- exports.estSeatProbability = estSeatProbability;
90010
90404
  // Estimate the number of responsive districts [R(d)], given a set of Vf's
90011
90405
  function estDistrictResponsiveness(Vf) {
90012
90406
  // Python: 1 - 4 * (est_seat_probability(vpi) - 0.5)**2
90013
90407
  return U.trim(1.0 - 4.0 * Math.pow((estSeatProbability(Vf) - 0.5), 2));
90014
90408
  }
90015
90409
  exports.estDistrictResponsiveness = estDistrictResponsiveness;
90016
- function inferSVpoints(Vf, VfArray) {
90410
+ function inferSVpoints(Vf, VfArray, range) {
90017
90411
  const nDistricts = VfArray.length;
90018
90412
  let SVpoints = [];
90019
90413
  for (let shiftedVf of shiftRange()) {
90020
90414
  const shiftedVPI = shiftDistricts(Vf, VfArray, shiftedVf);
90021
- const shiftedSf = estProbableSeats(shiftedVPI) / nDistricts;
90415
+ const shiftedSf = estProbableSeats(shiftedVPI, range) / nDistricts;
90022
90416
  SVpoints.push({ v: shiftedVf, s: shiftedSf });
90023
90417
  }
90024
90418
  return SVpoints;
@@ -90056,8 +90450,9 @@ function shiftRange() {
90056
90450
  }
90057
90451
  // ESTIMATE BIAS ("FAIR")
90058
90452
  // ^S# - The # of Democratic seats closest to proportional @ statewide Vf
90453
+ // The "expected number of seats" from http://bit.ly/2Fcuf4q
90059
90454
  function bestSeats(N, Vf) {
90060
- return Math.round(N * Vf);
90455
+ return Math.round((N * Vf) - S.EPSILON);
90061
90456
  }
90062
90457
  exports.bestSeats = bestSeats;
90063
90458
  // ^S% - The corresponding Democratic seat share
@@ -90066,9 +90461,9 @@ function bestSeatShare(bestS, N) {
90066
90461
  }
90067
90462
  exports.bestSeatShare = bestSeatShare;
90068
90463
  // S# - The estimated # of Democratic seats @ statewide Vf, using seat probabilities
90069
- function estProbableSeats(VfArray) {
90464
+ function estProbableSeats(VfArray, range) {
90070
90465
  // Python: sum([est_seat_probability(vpi) for vpi in vpi_by_district])
90071
- return U.trim(U.sumArray(VfArray.map(v => estSeatProbability(v))));
90466
+ return U.trim(U.sumArray(VfArray.map(v => estSeatProbability(v, range))));
90072
90467
  }
90073
90468
  exports.estProbableSeats = estProbableSeats;
90074
90469
  // S% - The estimated Democratic seat share fraction @ statewide Vf
@@ -90094,6 +90489,13 @@ function estBias(estSeatShare, bestSeatShare) {
90094
90489
  return U.trim(Math.abs(estSeatShare - bestSeatShare));
90095
90490
  }
90096
90491
  exports.estBias = estBias;
90492
+ // UE# - The estimated # of unearned seats
90493
+ // UE_# from http://bit.ly/2Fcuf4q
90494
+ function estUnearnedSeats(best, probable) {
90495
+ // NOTE - + values = unearned R seats; – values = unearned D seats
90496
+ return U.trim(best - probable);
90497
+ }
90498
+ exports.estUnearnedSeats = estUnearnedSeats;
90097
90499
  // ESTIMATE RESPONSIVENESS ("COMPETITIVE")
90098
90500
  // R# - Estimate responsiveness at the statewide vote share
90099
90501
  function estResponsiveness(Vf, inferredSVpoints) {
@@ -90148,6 +90550,14 @@ function estResponsiveDistrictsShare(Rd, N) {
90148
90550
  }
90149
90551
  exports.estResponsiveDistrictsShare = estResponsiveDistrictsShare;
90150
90552
  // ESTIMATE COMPETITIVENESS (ENHANCED)
90553
+ // C - Count the # of competitive districts, defined as v in [45–55%]
90554
+ function countCompetitiveDistricts(VfArray) {
90555
+ return U.trim(U.sumArray(VfArray.map(v => isCompetitive(v))));
90556
+ }
90557
+ exports.countCompetitiveDistricts = countCompetitiveDistricts;
90558
+ function isCompetitive(v) {
90559
+ return ((v >= C.competitiveRange()[C.BEG]) && (v <= C.competitiveRange()[C.END])) ? 1 : 0;
90560
+ }
90151
90561
  // Cd - The estimated # of competitive districts
90152
90562
  function estCompetitiveDistricts(VfArray) {
90153
90563
  return U.trim(U.sumArray(VfArray.map(v => estDistrictCompetitiveness(v))));
@@ -90156,8 +90566,14 @@ exports.estCompetitiveDistricts = estCompetitiveDistricts;
90156
90566
  // Re-scale a Democratic vote share to the competitive range (e.g., 45–55%).
90157
90567
  function estDistrictCompetitiveness(Vf) {
90158
90568
  const _normalizer = new normalize_1.Normalizer(Vf);
90159
- _normalizer.clip(S.MIN_CONTESTED, S.MAX_CONTESTED);
90160
- _normalizer.unitize(S.MIN_CONTESTED, S.MAX_CONTESTED);
90569
+ // The end points of a compressed probability distribution
90570
+ // NOTE - These aren't the points where races start or stop being contested,
90571
+ // just the end points of a distribution that yields the desired behavior
90572
+ // in the typical competitive range [45-55%].
90573
+ const distBeg = C.competitiveDistribution()[C.BEG];
90574
+ const distEnd = C.competitiveDistribution()[C.END];
90575
+ _normalizer.clip(distBeg, distEnd);
90576
+ _normalizer.unitize(distBeg, distEnd);
90161
90577
  const dC = estDistrictResponsiveness(_normalizer.wipNum);
90162
90578
  return dC;
90163
90579
  }
@@ -90167,9 +90583,30 @@ function estCompetitiveDistrictsShare(Cd, N) {
90167
90583
  return U.trim(Cd / N);
90168
90584
  }
90169
90585
  exports.estCompetitiveDistrictsShare = estCompetitiveDistrictsShare;
90586
+ // Md - The estimated # of "marginal" districts in and around the likely FPTP
90587
+ // seats & the best seat split that are competitive.
90588
+ function estMarginalCompetitiveDistricts(Mrange, VfArray) {
90589
+ const minId = Mrange[C.BEG];
90590
+ const maxId = Mrange[C.END];
90591
+ // Sort the array values, and subset it to those districts
90592
+ // NOTE - I'm *not* keeping track of the district indexes right now
90593
+ let subsetVfArray = VfArray.sort().slice(minId - 1, maxId);
90594
+ // Est. competitive districts on that array
90595
+ const Md = U.sumArray(subsetVfArray.map(v => estDistrictCompetitiveness(v)));
90596
+ return U.trim(Md);
90597
+ }
90598
+ exports.estMarginalCompetitiveDistricts = estMarginalCompetitiveDistricts;
90170
90599
  // Md% - The estimated competitiveness of the "marginal" districts in and around
90171
90600
  // the likely FPTP seats & the best seat split as a fraction
90172
- function estMarginalCompetitiveShare(Vf, VfArray, N) {
90601
+ function estMarginalCompetitiveShare(Md, Mrange) {
90602
+ const minId = Mrange[C.BEG];
90603
+ const maxId = Mrange[C.END];
90604
+ // Est. competitive district share on that result
90605
+ const MdShare = U.trim(estCompetitiveDistrictsShare(Md, maxId - minId + 1));
90606
+ return U.trim(MdShare);
90607
+ }
90608
+ exports.estMarginalCompetitiveShare = estMarginalCompetitiveShare;
90609
+ function findMarginalDistricts(Vf, VfArray, N) {
90173
90610
  const bestS = bestSeats(N, Vf);
90174
90611
  const fptpS = estFPTPSeats(VfArray);
90175
90612
  // Find the marginal districts IDs (indexed 1–N)
@@ -90188,24 +90625,16 @@ function estMarginalCompetitiveShare(Vf, VfArray, N) {
90188
90625
  minId = Math.max((N - bestS) - 1, 1);
90189
90626
  maxId = Math.min((N - bestS) + 1, N);
90190
90627
  }
90191
- const deltaS = maxId - minId + 1;
90192
- // Sort the array values, and subset it to those districts
90193
- // NOTE - I'm *not* keeping track of the district indexes right now
90194
- let subsetVfArray = VfArray.sort().slice(minId - 1, maxId);
90195
- // Est. competitive districts on that array
90196
- const Md = U.sumArray(subsetVfArray.map(v => estDistrictCompetitiveness(v)));
90197
- // Est. competitive district share on that result
90198
- const MdShare = U.trim(estCompetitiveDistrictsShare(Md, deltaS));
90199
- return U.trim(MdShare);
90628
+ return [minId, maxId];
90200
90629
  }
90201
- exports.estMarginalCompetitiveShare = estMarginalCompetitiveShare;
90630
+ exports.findMarginalDistricts = findMarginalDistricts;
90202
90631
 
90203
90632
 
90204
90633
  /***/ }),
90205
90634
 
90206
- /***/ "./src/index.ts":
90635
+ /***/ "./src/score.ts":
90207
90636
  /*!**********************!*\
90208
- !*** ./src/index.ts ***!
90637
+ !*** ./src/score.ts ***!
90209
90638
  \**********************/
90210
90639
  /*! no static exports found */
90211
90640
  /***/ (function(module, exports, __webpack_require__) {
@@ -90213,148 +90642,29 @@ exports.estMarginalCompetitiveShare = estMarginalCompetitiveShare;
90213
90642
  "use strict";
90214
90643
 
90215
90644
  //
90216
- // SCORE PACKAGE API
90645
+ // OVERALL SCORING FRAMEWORK
90217
90646
  //
90218
- function __export(m) {
90219
- for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
90220
- }
90647
+ var __importStar = (this && this.__importStar) || function (mod) {
90648
+ if (mod && mod.__esModule) return mod;
90649
+ var result = {};
90650
+ if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
90651
+ result["default"] = mod;
90652
+ return result;
90653
+ };
90221
90654
  Object.defineProperty(exports, "__esModule", { value: true });
90222
- __export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
90223
- var types_1 = __webpack_require__(/*! ./types */ "./src/types.ts");
90224
- exports.sampleProfile = types_1.sampleProfile;
90225
- exports.sampleScorecard = types_1.sampleScorecard;
90226
-
90227
-
90228
- /***/ }),
90229
-
90230
- /***/ "./src/normalize.ts":
90231
- /*!**************************!*\
90232
- !*** ./src/normalize.ts ***!
90233
- \**************************/
90234
- /*! no static exports found */
90235
- /***/ (function(module, exports, __webpack_require__) {
90236
-
90237
- "use strict";
90238
-
90239
- //
90240
- // NORMALIZATION UTILITIES
90241
- //
90242
- var __importStar = (this && this.__importStar) || function (mod) {
90243
- if (mod && mod.__esModule) return mod;
90244
- var result = {};
90245
- if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
90246
- result["default"] = mod;
90247
- return result;
90248
- };
90249
- Object.defineProperty(exports, "__esModule", { value: true });
90250
- const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
90251
- class Normalizer {
90252
- constructor(rawValue) {
90253
- this.rawNum = rawValue;
90254
- this.wipNum = this.rawNum;
90255
- }
90256
- // *Don't* transform the input.
90257
- identity() {
90258
- return this.wipNum;
90259
- }
90260
- // TODO - Do I need this? If so, add TEST cases.
90261
- positive() {
90262
- this.wipNum = Math.abs(this.wipNum);
90263
- return this.wipNum;
90264
- }
90265
- // Invert a value in the unit range [0.0–1.0] (so that bigger is better).
90266
- invert() {
90267
- // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90268
- console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), S.OUT_OF_RANGE_MSG);
90269
- this.wipNum = 1.0 - this.wipNum;
90270
- return this.wipNum;
90271
- }
90272
- // Constrain the value to be within a specified range.
90273
- clip(begin_range, end_range) {
90274
- // Handle the ends of the range being given either order
90275
- const min_range = Math.min(begin_range, end_range);
90276
- const max_range = Math.max(begin_range, end_range);
90277
- this.wipNum = Math.max(Math.min(this.wipNum, max_range), min_range);
90278
- return this.wipNum;
90279
- }
90280
- // Recast the value as the delta from a baseline value. NOOP if it is zero.
90281
- // NOTE - Values can be + or -.
90282
- rebase(base) {
90283
- this.wipNum = this.wipNum - base;
90284
- return this.wipNum;
90285
- }
90286
- // Re-scale a value into the [0.0 – 1.0] range, using a given range.
90287
- // NOTE - This assumes that values have alrady been clipped into the range.
90288
- unitize(begin_range, end_range) {
90289
- const min_range = Math.min(begin_range, end_range);
90290
- const max_range = Math.max(begin_range, end_range);
90291
- // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90292
- console.assert(((this.wipNum >= min_range) && (this.wipNum <= max_range)), S.OUT_OF_RANGE_MSG);
90293
- const ranged = this.wipNum - min_range;
90294
- this.wipNum = Math.abs(ranged / (end_range - begin_range));
90295
- return this.wipNum;
90296
- }
90297
- // Decay a value in the unit range [0.0–1.0] by its distance from zero.
90298
- // NOTE - If the range is already such that "bigger is better," then the closer
90299
- // the value is to 1.0 (the best) the *less* it will decay.
90300
- decay(fn = 0 /* Gravity */) {
90301
- // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90302
- console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), S.OUT_OF_RANGE_MSG);
90303
- switch (fn) {
90304
- case 0 /* Gravity */: {
90305
- this.wipNum = Math.pow(this.wipNum, S.DISTANCE_WEIGHT);
90306
- return this.wipNum;
90307
- }
90308
- default: {
90309
- throw new Error("Decay function not recognized.");
90310
- }
90311
- }
90312
- }
90313
- // Translate a value in the unit range to the user-friendly range [0 – 100].
90314
- rescale() {
90315
- // TODO - DAVID: Can we make this throw an Error and expect that in unit test?
90316
- console.assert(((this.wipNum >= 0.0) && (this.wipNum <= 1.0)), S.OUT_OF_RANGE_MSG);
90317
- this.normalizedNum = Math.round(this.wipNum * S.NORMALIZED_RANGE);
90318
- return this.normalizedNum;
90319
- }
90320
- }
90321
- exports.Normalizer = Normalizer;
90322
-
90323
-
90324
- /***/ }),
90325
-
90326
- /***/ "./src/score.ts":
90327
- /*!**********************!*\
90328
- !*** ./src/score.ts ***!
90329
- \**********************/
90330
- /*! no static exports found */
90331
- /***/ (function(module, exports, __webpack_require__) {
90332
-
90333
- "use strict";
90334
-
90335
- //
90336
- // OVERALL SCORING FRAMEWORK
90337
- //
90338
- var __importStar = (this && this.__importStar) || function (mod) {
90339
- if (mod && mod.__esModule) return mod;
90340
- var result = {};
90341
- if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
90342
- result["default"] = mod;
90343
- return result;
90344
- };
90345
- Object.defineProperty(exports, "__esModule", { value: true });
90346
- const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
90655
+ const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
90656
+ const C = __importStar(__webpack_require__(/*! ./config */ "./src/config.ts"));
90347
90657
  const compact_1 = __webpack_require__(/*! ./compact */ "./src/compact.ts");
90348
90658
  const cohesive_1 = __webpack_require__(/*! ./cohesive */ "./src/cohesive.ts");
90349
90659
  const equal_1 = __webpack_require__(/*! ./equal */ "./src/equal.ts");
90350
- const fair_1 = __webpack_require__(/*! ./fair */ "./src/fair.ts");
90660
+ const partisan_1 = __webpack_require__(/*! ./partisan */ "./src/partisan.ts");
90351
90661
  // TODO - Score opportunity for minority representation
90352
- function scorePlan(p) {
90353
- // BEST subcategories - compactness, splitting, population deviation
90662
+ function scorePlan(p, overridesJSON) {
90663
+ // TRADITIONAL DISTRICTING PRINCIPLES ("best") subcategories - compactness, splitting, population deviation
90354
90664
  // Compactness
90355
90665
  const reockM = compact_1.doReock(p.compactnessProfile.GeometryByDistrict);
90356
90666
  const polsbyM = compact_1.doPolsbyPopper(p.compactnessProfile.GeometryByDistrict);
90357
- const compactnessScore = Math.round(((S.REOCK_WEIGHT * reockM.normalized) + (S.POLSBY_WEIGHT * polsbyM.normalized)) / (S.REOCK_WEIGHT + S.POLSBY_WEIGHT));
90667
+ const compactnessScore = weightCompactness(reockM.normalized, polsbyM.normalized);
90358
90668
  const cS = {
90359
90669
  score: compactnessScore,
90360
90670
  reock: reockM,
@@ -90366,51 +90676,77 @@ function scorePlan(p) {
90366
90676
  const cT = cohesive_1.totalCounties(CxD);
90367
90677
  const countyM = cohesive_1.doCountySplittingReduced(CxD, dT, cT);
90368
90678
  const districtM = cohesive_1.doDistrictSplittingReduced(CxD, dT, cT);
90369
- const splittingScore = Math.round(((S.COUNTY_WEIGHT * countyM.normalized) + (S.DISTANCE_WEIGHT * districtM.normalized)) / (S.COUNTY_WEIGHT + S.DISTANCE_WEIGHT));
90679
+ const splittingScore = weightSplitting(countyM.normalized, districtM.normalized);
90370
90680
  const sS = {
90371
90681
  score: splittingScore,
90372
90682
  county: countyM,
90373
90683
  district: districtM
90374
90684
  };
90375
90685
  // Population deviation
90376
- const eS = equal_1.doPopulationDeviation(p.populationProfile.TotalPopByDistrict, p.populationProfile.targetSize, p.legislativeDistricts);
90377
- // Combine compactness, splitting, & population deviation into a "best" score
90378
- const bestScore = Math.round(((S.COMPACTNESS_WEIGHT * cS.score) + (S.SPLITTING_WEIGHT * sS.score) + (S.POPDEV_WEIGHT * eS.normalized)) / (S.COMPACTNESS_WEIGHT + S.SPLITTING_WEIGHT + S.POPDEV_WEIGHT));
90379
- // Populate the "best" scorecard
90380
- const bS = {
90381
- BestScore: bestScore,
90686
+ const pdS = equal_1.doPopulationDeviation(p.populationProfile.TotalPopByDistrict, p.populationProfile.targetSize, p.legislativeDistricts);
90687
+ // Combine traditional principles into a score to compare plans w/in a state
90688
+ const tpScore = weightTradtionalPrinciples(cS.score, sS.score, pdS.normalized, 1 /* WithinAState */);
90689
+ // Populate the "best" traditional principles scorecard
90690
+ const tpS = {
90691
+ score: tpScore,
90382
90692
  compactness: cS,
90383
90693
  splitting: sS,
90384
- populationDeviation: eS
90694
+ populationDeviation: pdS
90385
90695
  };
90386
- // FAIR subcategories - bias & competitiveness
90387
- // NOTE - This calculates a # of measures of bias & competitiveness.
90388
- const pS = fair_1.scorePartisan(p.partisanProfile.statewideVf, p.partisanProfile.VfArray);
90389
- pS.score = fair_1.weightPartisan(pS.bias.normalized, pS.competitiveness.normalized);
90390
- // Combine "best" and "fair" scores into an overall score for the plan
90391
- const score = Math.round(((S.FAIR_WEIGHT * pS.score) + (S.BEST_WEIGHT * bS.BestScore)) / (S.FAIR_WEIGHT + S.BEST_WEIGHT));
90696
+ // PARTISAN ("fair") subcategories - bias, impact, & competitiveness (plus lots of supporting measures)
90697
+ const pS = partisan_1.scorePartisan(p.partisanProfile.statewideVf, p.partisanProfile.VfArray);
90698
+ // Combine the partisan/partisan & traditional principles/best scores into an overall
90699
+ // score for comparing plans w/in a state
90700
+ const score = weightBestFair(pS.score2, tpS.score, 1 /* WithinAState */);
90392
90701
  // Roll up an overall scorecard
90393
90702
  const scorecard = {
90394
- score: score,
90395
- best: bS,
90396
- fair: pS
90703
+ partisan: pS,
90704
+ traditionalPrinciples: tpS,
90705
+ score: score
90397
90706
  };
90398
90707
  return scorecard;
90399
90708
  }
90400
90709
  exports.scorePlan = scorePlan;
90710
+ // HELPERS
90711
+ function weightCompactness(rS, ppS) {
90712
+ const rW = C.reockWeight();
90713
+ const ppW = C.polsbyWeight();
90714
+ const score = Math.round(((rS * rW) + (ppS * ppW)) / (rW + ppW));
90715
+ return score;
90716
+ }
90717
+ exports.weightCompactness = weightCompactness;
90718
+ function weightSplitting(csS, dsS) {
90719
+ const csW = C.countySplittingWeight();
90720
+ const dsW = C.districtSplittingWeight();
90721
+ const score = Math.round(((csS * csW) + (dsS * dsW)) / (csW + dsW));
90722
+ return score;
90723
+ }
90724
+ exports.weightSplitting = weightSplitting;
90725
+ function weightTradtionalPrinciples(cS, sS, pdS, context) {
90726
+ const cW = C.compactnessWeight(context);
90727
+ const sW = C.splittingWeight(context);
90728
+ const pdW = C.popdevWeight(context);
90729
+ const score = Math.round(((cS * cW) + (sS * sW) + (pdS * pdW)) / (cW + sW + pdW));
90730
+ return score;
90731
+ }
90732
+ exports.weightTradtionalPrinciples = weightTradtionalPrinciples;
90733
+ function weightBestFair(pS, tpS, context) {
90734
+ const pW = C.partisanWeight(context);
90735
+ const tpW = C.traditionalPrinciplesWeight(context);
90736
+ const score = Math.round(((pS * pW) + (tpS * tpW)) / (pW + tpW));
90737
+ return score;
90738
+ }
90739
+ exports.weightBestFair = weightBestFair;
90740
+ function printScorecardHeader() {
90741
+ console.log('XX, Name, N, V%, ^S#, S#, B%, B$, UE#, I$, C#, Cd, Md, C$, <P$, >P$, Rc, Rc$, Pc, Pc$, G$, Cs, Cs$, Ds, Ds$, S$, Eq, Eq$, T$, $$$');
90742
+ }
90743
+ exports.printScorecardHeader = printScorecardHeader;
90744
+ function printScorecardRow(xx, name, N, Vf, s) {
90745
+ console.log('%s, %s, %i, %f, %i, %f, %f, %i, %f, %i, %i, %f, %f, %i, %i, %i, %f, %i, %f, %i, %i, %f, %i, %f, %i, %i, %f, %i, %i, %i', xx, name, N, U.trim(Vf), s.partisan.bias.BestS, s.partisan.bias.ProbableS, s.partisan.bias.Bias, s.partisan.bias.score, s.partisan.impact.UnearnedS, s.partisan.impact.score, s.partisan.competitiveness.C, s.partisan.competitiveness.Cd, s.partisan.competitiveness.Md, s.partisan.competitiveness.score, s.partisan.score, s.partisan.score2, s.traditionalPrinciples.compactness.reock.raw, s.traditionalPrinciples.compactness.reock.normalized, s.traditionalPrinciples.compactness.polsby.raw, s.traditionalPrinciples.compactness.polsby.normalized, s.traditionalPrinciples.compactness.score, s.traditionalPrinciples.splitting.county.raw, s.traditionalPrinciples.splitting.county.normalized, s.traditionalPrinciples.splitting.district.raw, s.traditionalPrinciples.splitting.district.normalized, s.traditionalPrinciples.splitting.score, s.traditionalPrinciples.populationDeviation.raw, s.traditionalPrinciples.populationDeviation.normalized, s.traditionalPrinciples.score, s.score);
90746
+ }
90747
+ exports.printScorecardRow = printScorecardRow;
90401
90748
 
90402
90749
 
90403
- /***/ }),
90404
-
90405
- /***/ "./src/scoring-defaults.json":
90406
- /*!***********************************!*\
90407
- !*** ./src/scoring-defaults.json ***!
90408
- \***********************************/
90409
- /*! exports provided: fair, best, default */
90410
- /***/ (function(module) {
90411
-
90412
- module.exports = JSON.parse("{\"fair\":{\"unbiased\":{\"worst\":0.1,\"best\":0,\"weight\":80},\"competitive\":{\"worst\":0,\"best\":0.67,\"weight\":20,\"min\":0.25,\"max\":0.75,\"marginal\":75,\"overall\":25},\"weight\":80},\"best\":{\"compact\":{\"reock\":{\"worst\":0.25,\"best\":0.5,\"weight\":50},\"polsby\":{\"worst\":0.1,\"best\":0.5,\"weight\":50},\"weight\":50},\"cohesive\":{\"county\":{\"best\":1,\"worst\":1.71,\"allowableSplitsMultiplier\":1.5,\"weight\":50},\"district\":{\"best\":1,\"worst\":1.5,\"weight\":50},\"weight\":50},\"equal\":{\"worst\":0.0075,\"best\":0.002,\"stateLeg\":{\"worst\":0.1},\"weight\":0},\"weight\":20}}");
90413
-
90414
90750
  /***/ }),
90415
90751
 
90416
90752
  /***/ "./src/settings.ts":
@@ -90425,16 +90761,13 @@ module.exports = JSON.parse("{\"fair\":{\"unbiased\":{\"worst\":0.1,\"best\":0,\
90425
90761
  //
90426
90762
  // GLOBAL CONSTANTS
90427
90763
  //
90428
- var __importDefault = (this && this.__importDefault) || function (mod) {
90429
- return (mod && mod.__esModule) ? mod : { "default": mod };
90430
- };
90431
90764
  Object.defineProperty(exports, "__esModule", { value: true });
90432
90765
  // Normalized scores [0-100]
90433
90766
  exports.NORMALIZED_RANGE = 100;
90434
90767
  // Square deviations from the ideal
90435
90768
  exports.DISTANCE_WEIGHT = 2;
90436
90769
  // Out of range message
90437
- exports.OUT_OF_RANGE_MSG = "# out of range";
90770
+ exports.OUT_OF_RANGE_MSG = "%f out of range";
90438
90771
  // A small delta to use when testing ranges of values
90439
90772
  exports.EPSILON = 1 / Math.pow(10, 6);
90440
90773
  // Keep 4 decimals for fractions [0.0–1.0], i.e., 2 for %'s [0–100]
@@ -90442,68 +90775,6 @@ exports.PRECISION = 4;
90442
90775
  // "Roughly equal" = average census block size / 2
90443
90776
  exports.AVERAGE_BLOCK_SIZE = 30;
90444
90777
  exports.EQUAL_TOLERANCE = exports.AVERAGE_BLOCK_SIZE / 2;
90445
- // SCORING PARAMETERS
90446
- const scoring_defaults_json_1 = __importDefault(__webpack_require__(/*! ./scoring-defaults.json */ "./src/scoring-defaults.json"));
90447
- function readOverrides() {
90448
- // TODO - TERRY: Replumb this to:
90449
- // - Respond to an environment variable or config.json, and
90450
- // - Read settings.json at runtime from S3
90451
- // - Otherwise return false/null, so the conditionals below work
90452
- // - Note the utils/settings interdependency
90453
- // return U.readJSON('scoring-overrides.json');
90454
- return undefined;
90455
- }
90456
- exports.readOverrides = readOverrides;
90457
- let overrides = readOverrides();
90458
- // SETTINGS FOR PARTISAN SCORING
90459
- exports.MIN_CONTESTED = overrides ? overrides.fair.competitive.min : scoring_defaults_json_1.default.fair.competitive.min;
90460
- exports.MAX_CONTESTED = overrides ? overrides.fair.competitive.max : scoring_defaults_json_1.default.fair.competitive.max;
90461
- // SCALES FOR NORMALIZING RAW VALUES
90462
- // Compactness scales
90463
- exports.REOCK_WORST = overrides ? overrides.best.compact.reock.worst : scoring_defaults_json_1.default.best.compact.reock.worst;
90464
- exports.REOCK_BEST = overrides ? overrides.best.compact.reock.best : scoring_defaults_json_1.default.best.compact.reock.best;
90465
- exports.POLSBY_WORST = overrides ? overrides.best.compact.polsby.worst : scoring_defaults_json_1.default.best.compact.polsby.worst;
90466
- exports.POLSBY_BEST = overrides ? overrides.best.compact.polsby.best : scoring_defaults_json_1.default.best.compact.polsby.best;
90467
- // County-District splitting scales (not inverted)
90468
- exports.COUNTY_BEST = overrides ? overrides.best.cohesive.county.best : scoring_defaults_json_1.default.best.cohesive.county.best;
90469
- exports.COUNTY_WORST = overrides ? overrides.best.cohesive.county.worst : scoring_defaults_json_1.default.best.cohesive.county.worst;
90470
- exports.ALLOWABLE_SPLITS_MULTIPLIER = overrides ? overrides.best.cohesive.county.allowableSplitsMultiplier : scoring_defaults_json_1.default.best.cohesive.county.allowableSplitsMultiplier;
90471
- exports.DISTRICT_BEST = overrides ? overrides.best.cohesive.district.best : scoring_defaults_json_1.default.best.cohesive.district.best;
90472
- exports.DISTRICT_WORST = overrides ? overrides.best.cohesive.district.worst : scoring_defaults_json_1.default.best.cohesive.district.worst;
90473
- // Population deviation thresholds & scales (inverted)
90474
- exports.POPDEV_WORST = overrides ? overrides.best.equal.worst : scoring_defaults_json_1.default.best.equal.worst;
90475
- exports.POPDEV_BEST = overrides ? overrides.best.equal.best : scoring_defaults_json_1.default.best.equal.best;
90476
- exports.POPEQ_MIN = 1.0 - exports.POPDEV_WORST;
90477
- exports.POPEQ_MAX = 1.0 - exports.POPDEV_BEST;
90478
- exports.POPDEV_WORST_LD = overrides ? overrides.best.equal.stateLeg.worst : scoring_defaults_json_1.default.best.equal.stateLeg.worst;
90479
- exports.POPDEV_BEST_LD = ((exports.POPDEV_BEST / exports.POPDEV_WORST) * exports.POPDEV_WORST_LD);
90480
- exports.POPEQ_MIN_LD = 1.0 - exports.POPDEV_WORST_LD;
90481
- exports.POPEQ_MAX_LD = 1.0 - exports.POPDEV_BEST_LD;
90482
- // Bias & competitiveness scales
90483
- // TODO - SCORE:
90484
- // * How wide a range do we want for bias? 10%? 20%?
90485
- // * Do we want it to be state-specific? E.g., one seat?
90486
- exports.BIAS_WORST = overrides ? overrides.fair.unbiased.worst : scoring_defaults_json_1.default.fair.unbiased.worst;
90487
- exports.BIAS_BEST = overrides ? overrides.fair.unbiased.best : scoring_defaults_json_1.default.fair.unbiased.best;
90488
- // The range for raw overall competitiveness values
90489
- exports.COMPETITIVE_WORST = overrides ? overrides.fair.competitive.worst : scoring_defaults_json_1.default.fair.competitive.worst;
90490
- exports.COMPETITIVE_BEST = overrides ? overrides.fair.competitive.best : scoring_defaults_json_1.default.fair.competitive.best;
90491
- // WEIGHTS FOR COMBINING NORMALIZED VALUES
90492
- exports.REOCK_WEIGHT = overrides ? overrides.best.compact.reock.weight : scoring_defaults_json_1.default.best.compact.reock.weight;
90493
- exports.POLSBY_WEIGHT = overrides ? overrides.best.compact.polsby.weight : scoring_defaults_json_1.default.best.compact.polsby.weight;
90494
- exports.COUNTY_WEIGHT = overrides ? overrides.best.cohesive.county.weight : scoring_defaults_json_1.default.best.cohesive.county.weight;
90495
- exports.DISTRICT_WEIGHT = overrides ? overrides.best.cohesive.district.weight : scoring_defaults_json_1.default.best.cohesive.district.weight;
90496
- exports.COMPACTNESS_WEIGHT = overrides ? overrides.best.compact.weight : scoring_defaults_json_1.default.best.compact.weight;
90497
- exports.SPLITTING_WEIGHT = overrides ? overrides.best.cohesive.weight : scoring_defaults_json_1.default.best.cohesive.weight;
90498
- exports.POPDEV_WEIGHT = overrides ? overrides.best.equal.weight : scoring_defaults_json_1.default.best.equal.weight;
90499
- // TODO - SCORE: What kind of relative weighting do we want between marginal and
90500
- // overall competitiveness?
90501
- exports.MARGINAL_WEIGHT = overrides ? overrides.fair.competitive.marginal : scoring_defaults_json_1.default.fair.competitive.marginal;
90502
- exports.OVERALL_WEIGHT = overrides ? overrides.fair.competitive.overall : scoring_defaults_json_1.default.fair.competitive.overall;
90503
- exports.BIAS_WEIGHT = overrides ? overrides.fair.unbiased.weight : scoring_defaults_json_1.default.fair.unbiased.weight;
90504
- exports.COMPETITIVE_WEIGHT = overrides ? overrides.fair.competitive.weight : scoring_defaults_json_1.default.fair.competitive.weight;
90505
- exports.FAIR_WEIGHT = overrides ? overrides.fair.weight : scoring_defaults_json_1.default.fair.weight;
90506
- exports.BEST_WEIGHT = overrides ? overrides.best.weight : scoring_defaults_json_1.default.best.weight;
90507
90778
 
90508
90779
 
90509
90780
  /***/ }),
@@ -90524,9 +90795,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
90524
90795
  return (mod && mod.__esModule) ? mod : { "default": mod };
90525
90796
  };
90526
90797
  Object.defineProperty(exports, "__esModule", { value: true });
90527
- const sample_profile_json_1 = __importDefault(__webpack_require__(/*! ../testdata/profiles/sample-profile.json */ "./testdata/profiles/sample-profile.json"));
90798
+ const sample_profile_json_1 = __importDefault(__webpack_require__(/*! ../testdata/samples/sample-profile.json */ "./testdata/samples/sample-profile.json"));
90528
90799
  exports.sampleProfile = sample_profile_json_1.default;
90529
- const sample_scorecard_json_1 = __importDefault(__webpack_require__(/*! ../testdata/profiles/sample-scorecard.json */ "./testdata/profiles/sample-scorecard.json"));
90800
+ const sample_scorecard_json_1 = __importDefault(__webpack_require__(/*! ../testdata/samples/sample-scorecard.json */ "./testdata/samples/sample-scorecard.json"));
90530
90801
  exports.sampleScorecard = sample_scorecard_json_1.default;
90531
90802
 
90532
90803
 
@@ -90603,35 +90874,1846 @@ function deepCopy(src) {
90603
90874
  else
90604
90875
  return src;
90605
90876
  }
90606
- exports.deepCopy = deepCopy;
90877
+ exports.deepCopy = deepCopy;
90878
+
90879
+
90880
+ /***/ }),
90881
+
90882
+ /***/ "./testdata/samples/sample-profile.json":
90883
+ /*!**********************************************!*\
90884
+ !*** ./testdata/samples/sample-profile.json ***!
90885
+ \**********************************************/
90886
+ /*! exports provided: state, planName, nDistricts, nCounties, legislativeDistricts, populationProfile, compactnessProfile, splittingProfile, partisanProfile, demographicProfile, default */
90887
+ /***/ (function(module) {
90888
+
90889
+ module.exports = JSON.parse("{\"state\":\"NC\",\"planName\":\"Sample profile\",\"nDistricts\":13,\"nCounties\":100,\"legislativeDistricts\":false,\"populationProfile\":{\"TotalPopByDistrict\":[725807,721754,732627,733218,730051,729580,728476,729721,731507,724085,733447,730845,729710],\"targetSize\":729294},\"compactnessProfile\":{\"GeometryByDistrict\":[[1.5773977911324972,10.187637000340999,2.6574300583946115],[0.7058475358440041,7.6543777040386445,1.7632772181381422],[2.920140664739499,9.983421957097065,2.733997011824872],[0.19139679136850038,3.5473804553379114,0.9224411598627837],[1.0389865962844946,6.505473449741405,2.1061393959539023],[1.0334553158144995,6.472595447414713,1.6394147493749138],[1.6241801669130118,8.040301088673953,1.952917034583555],[0.7706765524270006,7.015983900119356,2.1886348192462464],[0.9976965229710011,8.405282685068,2.5604335810665138],[0.676265120610002,5.866361656441843,1.7264712385837822],[1.719226531340005,10.410671767954964,3.1586859214584764],[0.11329460131899982,2.3038812649606695,0.5379072920877442],[0.48373856130249987,5.267369326010034,1.45173753753012]]},\"splittingProfile\":{\"CountyPopByDistrict\":[[0,0,0,0,0,0,0,0,21282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240299,56552,0,0,0,12197,0,59916,0,0,54691,0,0,0,24669,0,0,0,0,0,0,0,0,0,0,0,0,24505,0,0,0,0,0,0,22099,0,0,0,0,0,0,0,74235,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45422,0,20972,13228,0,0,0,55740,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60619,0,0,0,0,0,0,0,114678,0,0,0,0,0,0,0,109245,0,0,0,0,0,0,0,0,0,0,0,0,95840,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,324879,0,0,0,0,0,16493,0,0],[0,0,0,0,0,0,0,47759,0,0,0,0,0,0,0,9980,66469,0,0,0,0,14793,0,0,0,103505,0,23547,33920,0,0,0,0,0,0,0,0,0,0,0,21362,0,0,0,0,0,0,0,5810,0,0,0,10153,0,59495,0,0,0,0,0,0,0,0,0,0,0,0,177772,0,13144,40661,0,13453,0,86397,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4407,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27288,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133801,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,572129,0,0,0,0,0,0,0,0],[0,0,37198,11155,0,27281,17797,0,0,0,0,0,0,0,0,0,0,0,6051,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,350670,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47401,73673,0,0,0,0,0,0,0,0,51079,0,69340,0,38406,0],[0,151131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23719,0,63505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158500,0,0,0,0,0,0,0,0,0,0,0,57866,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39464,0,0,141752,0,0,93643,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,8316,107431,0,0,0,0,0,0,0,0,0,0,0,0,0,58098,0,0,0,0,0,0,58505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55188,0,0,0,0,0,0,0,0,0,0,0,0,0,202667,0,0,0,0,0,52217,0,0,0,0,0,0,0,0,0,0,63431,0,0,0,0,0,0,0,0,0,0,0,0,0,122623,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,178011,0,0,0,0,0,0,0,0,0,0,0,0,242290,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46952,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27798,88247,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85838,0,0,0,60585,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,26948,0,0,0,0,25045,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185734,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46639,134168,0,0,0,0,36157,0,0,0,0,0,0,201292,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,108857,0,0,0,0,0,0,144479,0,0,0,0,98078,0,0,0,0,0,0,0,0,0,0,0,0,206086,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78265,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20510,0,0,0,0,0,67810,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,126417,90912,0,83029,0,0,0,0,0,27444,0,10587,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8861,0,0,0,0,0,59036,106740,0,0,0,0,40271,0,0,0,0,0,44996,33922,20764,0,0,15579,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13981,33090,0,0,0,0,0,0,0,0,0,0,0,17818],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,730845,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,162878,41240,0,0,0,0,0,0,0,0,0,0,325932,0,0,0,0,0,0,0,150509,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49151,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]},\"partisanProfile\":{\"statewideVf\":0.515036,\"VfArray\":[0.4235,0.428588,0.43388,0.443866,0.454505,0.456985,0.458005,0.458134,0.463947,0.473144,0.718314,0.73662,0.775817]},\"demographicProfile\":{\"DemographicsByDistrict\":[[0.462,0.538,0.0659,0.4445,0.0219,0.0009,0.013],[0.7063,0.2937,0.0714,0.1968,0.0203,0.001,0.0113],[0.7125,0.2875,0.0537,0.2118,0.0163,0.0021,0.0104],[0.6162,0.3838,0.0869,0.2239,0.0715,0.0012,0.0101],[0.7767,0.2233,0.0669,0.1402,0.013,0.0008,0.0077],[0.7112,0.2888,0.0715,0.1985,0.0136,0.0008,0.0102],[0.7086,0.2914,0.0695,0.2023,0.0097,0.0011,0.0142],[0.6698,0.3302,0.0725,0.224,0.0229,0.0029,0.0188],[0.6406,0.3594,0.0594,0.1962,0.0222,0.001,0.0862],[0.8193,0.1807,0.0451,0.1158,0.0141,0.0007,0.008],[0.8956,0.1044,0.0433,0.0327,0.0103,0.0012,0.0197],[0.4657,0.5343,0.1215,0.3618,0.0507,0.0017,0.011],[0.698,0.302,0.0572,0.2117,0.0286,0.0009,0.0095]]}}");
90890
+
90891
+ /***/ }),
90892
+
90893
+ /***/ "./testdata/samples/sample-scorecard.json":
90894
+ /*!************************************************!*\
90895
+ !*** ./testdata/samples/sample-scorecard.json ***!
90896
+ \************************************************/
90897
+ /*! exports provided: score, traditionalPrinciples, partisan, default */
90898
+ /***/ (function(module) {
90899
+
90900
+ module.exports = JSON.parse("{\"score\":5,\"traditionalPrinciples\":{\"score\":18,\"compactness\":{\"score\":35,\"reock\":{\"raw\":0.3373,\"normalized\":35,\"notes\":{}},\"polsby\":{\"raw\":0.2418,\"normalized\":35,\"notes\":{}}},\"splitting\":{\"score\":0,\"county\":{\"raw\":1.1474,\"normalized\":0,\"notes\":{}},\"district\":{\"raw\":1.4839,\"normalized\":3,\"notes\":{}}},\"populationDeviation\":{\"raw\":0.016,\"normalized\":0,\"notes\":{\"maxDeviation\":11693}}},\"partisan\":{\"bias\":{\"BestS\":7,\"BestSf\":0.5385,\"ProbableS\":4.1925,\"ProbableSf\":0.3225,\"Bias\":0.216,\"score\":0},\"impact\":{\"UnearnedS\":2.8075,\"score\":0},\"competitiveness\":{\"C\":6,\"Cd\":0.7266,\"Cdf\":0.0559,\"Mrange\":[5,11],\"Md\":0.7134,\"Mdf\":0.1019,\"score\":10},\"score\":3,\"score2\":2}}");
90901
+
90902
+ /***/ })
90903
+
90904
+ /******/ });
90905
+ });
90906
+
90907
+
90908
+ /***/ }),
90909
+
90910
+ /***/ "./node_modules/@dra2020/dra-types/dist/dra-types.js":
90911
+ /*!***********************************************************!*\
90912
+ !*** ./node_modules/@dra2020/dra-types/dist/dra-types.js ***!
90913
+ \***********************************************************/
90914
+ /*! no static exports found */
90915
+ /***/ (function(module, exports, __webpack_require__) {
90916
+
90917
+ (function webpackUniversalModuleDefinition(root, factory) {
90918
+ if(true)
90919
+ module.exports = factory();
90920
+ else {}
90921
+ })(global, function() {
90922
+ return /******/ (function(modules) { // webpackBootstrap
90923
+ /******/ // The module cache
90924
+ /******/ var installedModules = {};
90925
+ /******/
90926
+ /******/ // The require function
90927
+ /******/ function __webpack_require__(moduleId) {
90928
+ /******/
90929
+ /******/ // Check if module is in cache
90930
+ /******/ if(installedModules[moduleId]) {
90931
+ /******/ return installedModules[moduleId].exports;
90932
+ /******/ }
90933
+ /******/ // Create a new module (and put it into the cache)
90934
+ /******/ var module = installedModules[moduleId] = {
90935
+ /******/ i: moduleId,
90936
+ /******/ l: false,
90937
+ /******/ exports: {}
90938
+ /******/ };
90939
+ /******/
90940
+ /******/ // Execute the module function
90941
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
90942
+ /******/
90943
+ /******/ // Flag the module as loaded
90944
+ /******/ module.l = true;
90945
+ /******/
90946
+ /******/ // Return the exports of the module
90947
+ /******/ return module.exports;
90948
+ /******/ }
90949
+ /******/
90950
+ /******/
90951
+ /******/ // expose the modules object (__webpack_modules__)
90952
+ /******/ __webpack_require__.m = modules;
90953
+ /******/
90954
+ /******/ // expose the module cache
90955
+ /******/ __webpack_require__.c = installedModules;
90956
+ /******/
90957
+ /******/ // define getter function for harmony exports
90958
+ /******/ __webpack_require__.d = function(exports, name, getter) {
90959
+ /******/ if(!__webpack_require__.o(exports, name)) {
90960
+ /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
90961
+ /******/ }
90962
+ /******/ };
90963
+ /******/
90964
+ /******/ // define __esModule on exports
90965
+ /******/ __webpack_require__.r = function(exports) {
90966
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
90967
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
90968
+ /******/ }
90969
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
90970
+ /******/ };
90971
+ /******/
90972
+ /******/ // create a fake namespace object
90973
+ /******/ // mode & 1: value is a module id, require it
90974
+ /******/ // mode & 2: merge all properties of value into the ns
90975
+ /******/ // mode & 4: return value when already ns object
90976
+ /******/ // mode & 8|1: behave like require
90977
+ /******/ __webpack_require__.t = function(value, mode) {
90978
+ /******/ if(mode & 1) value = __webpack_require__(value);
90979
+ /******/ if(mode & 8) return value;
90980
+ /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
90981
+ /******/ var ns = Object.create(null);
90982
+ /******/ __webpack_require__.r(ns);
90983
+ /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
90984
+ /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
90985
+ /******/ return ns;
90986
+ /******/ };
90987
+ /******/
90988
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
90989
+ /******/ __webpack_require__.n = function(module) {
90990
+ /******/ var getter = module && module.__esModule ?
90991
+ /******/ function getDefault() { return module['default']; } :
90992
+ /******/ function getModuleExports() { return module; };
90993
+ /******/ __webpack_require__.d(getter, 'a', getter);
90994
+ /******/ return getter;
90995
+ /******/ };
90996
+ /******/
90997
+ /******/ // Object.prototype.hasOwnProperty.call
90998
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
90999
+ /******/
91000
+ /******/ // __webpack_public_path__
91001
+ /******/ __webpack_require__.p = "";
91002
+ /******/
91003
+ /******/
91004
+ /******/ // Load entry module and return exports
91005
+ /******/ return __webpack_require__(__webpack_require__.s = "./lib/all.ts");
91006
+ /******/ })
91007
+ /************************************************************************/
91008
+ /******/ ({
91009
+
91010
+ /***/ "./lib/all.ts":
91011
+ /*!********************!*\
91012
+ !*** ./lib/all.ts ***!
91013
+ \********************/
91014
+ /*! no static exports found */
91015
+ /***/ (function(module, exports, __webpack_require__) {
91016
+
91017
+ "use strict";
91018
+
91019
+ function __export(m) {
91020
+ for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
91021
+ }
91022
+ Object.defineProperty(exports, "__esModule", { value: true });
91023
+ __export(__webpack_require__(/*! ./dra-types */ "./lib/dra-types.ts"));
91024
+
91025
+
91026
+ /***/ }),
91027
+
91028
+ /***/ "./lib/dra-types.ts":
91029
+ /*!**************************!*\
91030
+ !*** ./lib/dra-types.ts ***!
91031
+ \**************************/
91032
+ /*! no static exports found */
91033
+ /***/ (function(module, exports, __webpack_require__) {
91034
+
91035
+ "use strict";
91036
+
91037
+ Object.defineProperty(exports, "__esModule", { value: true });
91038
+ // Public libraries
91039
+ const Hash = __webpack_require__(/*! object-hash */ "object-hash");
91040
+ const Util = __webpack_require__(/*! @dra2020/util */ "@dra2020/util");
91041
+ // Canonical hashing of splitblock data
91042
+ function hash(o) {
91043
+ return Hash(o, { respectType: false,
91044
+ unorderedArrays: true,
91045
+ unorderedObjects: true,
91046
+ excludeKeys: (k) => (k === 'id' || k === 'chunk')
91047
+ });
91048
+ }
91049
+ function vgeoidToGeoid(vgeoid) {
91050
+ let re = /vfeature_([^_]*)_.*/;
91051
+ let a = re.exec(vgeoid);
91052
+ if (a == null || a.length != 2)
91053
+ return '';
91054
+ else
91055
+ return a[1];
91056
+ }
91057
+ exports.vgeoidToGeoid = vgeoidToGeoid;
91058
+ function vgeoidToChunk(vgeoid) {
91059
+ // vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
91060
+ // the contents are chunked into a file of form "vfeature_chunk_[chunkid]"
91061
+ // So extract the chunk ID and download that.
91062
+ let re = /vfeature_([^_]*)_([^_*])_(.*)/;
91063
+ let a = re.exec(vgeoid);
91064
+ if (a && a.length == 4)
91065
+ vgeoid = `vfeature_chunk_${a[2]}`;
91066
+ else
91067
+ vgeoid = null;
91068
+ return vgeoid;
91069
+ }
91070
+ exports.vgeoidToChunk = vgeoidToChunk;
91071
+ function vgeoidToHash(vgeoid) {
91072
+ // vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
91073
+ let re = /vfeature_([^_]*)_([^_*])_(.*)/;
91074
+ let a = re.exec(vgeoid);
91075
+ if (a && a.length == 4)
91076
+ vgeoid = a[3];
91077
+ else
91078
+ vgeoid = null;
91079
+ return vgeoid;
91080
+ }
91081
+ exports.vgeoidToHash = vgeoidToHash;
91082
+ function isVfeature(geoid) {
91083
+ return geoid.indexOf('vfeature') === 0;
91084
+ }
91085
+ exports.isVfeature = isVfeature;
91086
+ function splitToCacheKey(s) {
91087
+ if (s.id === undefined)
91088
+ s.id = hash(s);
91089
+ if (s.chunk === undefined)
91090
+ s.chunk = "0";
91091
+ return `_${s.state}_${s.datasource}_vfeature_${s.geoid}_${s.chunk}_${s.id}.geojson`;
91092
+ }
91093
+ exports.splitToCacheKey = splitToCacheKey;
91094
+ function splitToChunkKey(s) {
91095
+ if (s.chunk === undefined)
91096
+ s.chunk = "0";
91097
+ return `_${s.state}_${s.datasource}_vfeature_chunk_${s.chunk}.geojson`;
91098
+ }
91099
+ exports.splitToChunkKey = splitToChunkKey;
91100
+ function splitToPrefix(s) {
91101
+ if (s.blocks === undefined) {
91102
+ let re = /_([^_]*)_(.*)_vfeature.*\.geojson$/;
91103
+ let a = re.exec(s.id);
91104
+ if (a && a.length == 3)
91105
+ return `_${a[1]}_${a[2]}`;
91106
+ return s.id;
91107
+ }
91108
+ return `_${s.state}_${s.datasource}`;
91109
+ }
91110
+ exports.splitToPrefix = splitToPrefix;
91111
+ function cacheKeysToChunkHash(keys) {
91112
+ return hash(keys);
91113
+ }
91114
+ exports.cacheKeysToChunkHash = cacheKeysToChunkHash;
91115
+ let reNumeric = /^(\D*)(\d*)(\D*)$/;
91116
+ let reDistrictNumber = /^\d+$/;
91117
+ let reDistrictNumeric = /^\d/;
91118
+ // Normalize any numeric part to have no padded leading zeros
91119
+ function canonicalDistrictID(districtID) {
91120
+ let a = reNumeric.exec(districtID);
91121
+ if (a && a.length == 4) {
91122
+ if (a[2].length > 0)
91123
+ a[2] = String(Number(a[2]));
91124
+ districtID = `${a[1]}${a[2]}${a[3]}`;
91125
+ }
91126
+ return districtID;
91127
+ }
91128
+ exports.canonicalDistrictID = canonicalDistrictID;
91129
+ // Normalize any numeric part to have four digits with padded leading zeros
91130
+ function canonicalSortingDistrictID(districtID) {
91131
+ let a = reNumeric.exec(districtID);
91132
+ if (a && a.length == 4) {
91133
+ let s = a[2];
91134
+ if (s.length > 0) {
91135
+ switch (s.length) {
91136
+ case 1:
91137
+ s = `000${s}`;
91138
+ break;
91139
+ case 2:
91140
+ s = `00${s}`;
91141
+ break;
91142
+ case 3:
91143
+ s = `0${s}`;
91144
+ break;
91145
+ }
91146
+ a[2] = s;
91147
+ }
91148
+ districtID = `${a[1]}${a[2]}${a[3]}`;
91149
+ }
91150
+ return districtID;
91151
+ }
91152
+ exports.canonicalSortingDistrictID = canonicalSortingDistrictID;
91153
+ // Return numeric part of districtID (or -1 if there is none)
91154
+ function canonicalNumericFromDistrictID(districtID) {
91155
+ let a = reNumeric.exec(districtID);
91156
+ if (a && a.length == 4) {
91157
+ let s = a[2];
91158
+ if (s.length > 0)
91159
+ return Number(s);
91160
+ }
91161
+ return -1;
91162
+ }
91163
+ exports.canonicalNumericFromDistrictID = canonicalNumericFromDistrictID;
91164
+ function canonicalDistrictIDFromNumber(districtID, n) {
91165
+ let a = reNumeric.exec(districtID);
91166
+ if (a && a.length == 4) {
91167
+ a[2] = String(n);
91168
+ districtID = `${a[1]}${a[2]}${a[3]}`;
91169
+ }
91170
+ else
91171
+ districtID = String(n);
91172
+ return districtID;
91173
+ }
91174
+ exports.canonicalDistrictIDFromNumber = canonicalDistrictIDFromNumber;
91175
+ function canonicalDistrictIDOrdering(order) {
91176
+ let keys = Object.keys(order);
91177
+ let i;
91178
+ let a = [];
91179
+ let template = undefined;
91180
+ keys = keys.map((s) => canonicalSortingDistrictID(s));
91181
+ keys.sort();
91182
+ order = {};
91183
+ for (i = 0; i < keys.length; i++)
91184
+ order[canonicalDistrictID(keys[i])] = i + 1;
91185
+ // Remove water districts
91186
+ if (order['ZZZ'])
91187
+ delete order['ZZZ'];
91188
+ if (order['ZZ'])
91189
+ delete order['ZZ'];
91190
+ return order;
91191
+ }
91192
+ exports.canonicalDistrictIDOrdering = canonicalDistrictIDOrdering;
91193
+ let reArray = [
91194
+ /^(\d\d[^\s,"']*)[\s]*,[\s]*([^\s'"]+)[\s]*$/,
91195
+ /^["'](\d\d[^"']*)["'][\s]*,[\s]*["']([^"']*)["'][\s]*$/,
91196
+ /^(\d\d[^\s,]*)[\s]*,[\s]*["']([^"']*)["'][\s]*$/,
91197
+ /^["'](\d\d[^"']*)["'][\s]*,[\s]*([^\s]+)[\s]*$/,
91198
+ ];
91199
+ function parseCSVLine(line) {
91200
+ if (line == null || line == '')
91201
+ return null;
91202
+ for (let i = 0; i < reArray.length; i++) {
91203
+ let a = reArray[i].exec(line);
91204
+ if (a && a.length === 3)
91205
+ return { geoid: a[1], districtID: a[2] };
91206
+ }
91207
+ return null;
91208
+ }
91209
+ exports.parseCSVLine = parseCSVLine;
91210
+ function blockmapToState(blockMap) {
91211
+ for (var id in blockMap)
91212
+ if (blockMap.hasOwnProperty(id))
91213
+ return geoidToState(id);
91214
+ return null;
91215
+ }
91216
+ exports.blockmapToState = blockmapToState;
91217
+ // blockToVTD:
91218
+ // Take BlockMapping (simple map of GEOID to districtID) and a per-state map of block-level GEOID to VTD
91219
+ // and return the output mapping of VTD to districtID, as well a data structure that describes any VTD's
91220
+ // that need to be split between districtIDs. Also returns the DistrictOrder structure that defines the
91221
+ // districtIDs that were used by the file.
91222
+ //
91223
+ // The state (as specified by the first two digits of the GEOID) is also determined. If the GEOID's do
91224
+ // not all specify the same state, the mapping is considered invalid and the outValid flag is set to false.
91225
+ //
91226
+ function blockmapToVTDmap(blockMap, stateMap) {
91227
+ let res = {
91228
+ inBlockMap: blockMap,
91229
+ inStateMap: stateMap,
91230
+ outValid: true,
91231
+ outState: null,
91232
+ outMap: {},
91233
+ outOrder: {},
91234
+ outDistrictToSplit: {}
91235
+ };
91236
+ let bmGather = {};
91237
+ let revMap = {};
91238
+ let id;
91239
+ if (stateMap)
91240
+ for (id in stateMap)
91241
+ if (stateMap.hasOwnProperty(id))
91242
+ revMap[stateMap[id]] = null;
91243
+ // First aggregate into features across all the blocks
91244
+ for (id in blockMap)
91245
+ if (blockMap.hasOwnProperty(id)) {
91246
+ let state = geoidToState(id);
91247
+ if (res.outState == null)
91248
+ res.outState = state;
91249
+ else if (res.outState !== state) {
91250
+ res.outValid = false;
91251
+ break;
91252
+ }
91253
+ let districtID = canonicalDistrictID(blockMap[id]);
91254
+ // Just ignore ZZZ (water) blocks
91255
+ if (districtID === 'ZZZ')
91256
+ continue;
91257
+ let n = id.length;
91258
+ let geoid;
91259
+ // Simple test for block id (vs. voting district or block group) id
91260
+ if (n >= 15) {
91261
+ if (stateMap && stateMap[id] !== undefined)
91262
+ geoid = stateMap[id];
91263
+ else {
91264
+ geoid = id.substr(0, 12); // heuristic for mapping blockID to blockgroupID
91265
+ if (revMap[geoid] === undefined) {
91266
+ res.outValid = false;
91267
+ break;
91268
+ }
91269
+ }
91270
+ }
91271
+ else
91272
+ geoid = id;
91273
+ if (res.outOrder[districtID] === undefined)
91274
+ res.outOrder[districtID] = 0;
91275
+ let districtToBlocks = bmGather[geoid];
91276
+ if (districtToBlocks === undefined)
91277
+ bmGather[geoid] = { [districtID]: { [id]: true } };
91278
+ else {
91279
+ let thisDistrict = districtToBlocks[districtID];
91280
+ if (thisDistrict === undefined) {
91281
+ thisDistrict = {};
91282
+ districtToBlocks[districtID] = thisDistrict;
91283
+ }
91284
+ thisDistrict[id] = true;
91285
+ }
91286
+ }
91287
+ // Now determine actual mapping of blocks to features, looking for split features
91288
+ for (let geoid in bmGather)
91289
+ if (bmGather.hasOwnProperty(geoid)) {
91290
+ let districtToBlocks = bmGather[geoid];
91291
+ if (Util.countKeys(districtToBlocks) == 1) {
91292
+ res.outMap[geoid] = Util.nthKey(districtToBlocks);
91293
+ }
91294
+ else {
91295
+ for (let districtID in districtToBlocks)
91296
+ if (districtToBlocks.hasOwnProperty(districtID)) {
91297
+ let split = { state: '', datasource: '', geoid: geoid, blocks: Object.keys(districtToBlocks[districtID]) };
91298
+ let splits = res.outDistrictToSplit[districtID];
91299
+ if (splits === undefined) {
91300
+ splits = [];
91301
+ res.outDistrictToSplit[districtID] = splits;
91302
+ }
91303
+ splits.push(split);
91304
+ }
91305
+ }
91306
+ }
91307
+ res.outOrder = canonicalDistrictIDOrdering(res.outOrder);
91308
+ return res;
91309
+ }
91310
+ exports.blockmapToVTDmap = blockmapToVTDmap;
91311
+ exports.GEOIDToState = {
91312
+ '01': 'AL',
91313
+ '02': 'AK',
91314
+ '04': 'AZ',
91315
+ '05': 'AR',
91316
+ '06': 'CA',
91317
+ '08': 'CO',
91318
+ '09': 'CT',
91319
+ '10': 'DE',
91320
+ '12': 'FL',
91321
+ '13': 'GA',
91322
+ '15': 'HI',
91323
+ '16': 'ID',
91324
+ '17': 'IL',
91325
+ '18': 'IN',
91326
+ '19': 'IA',
91327
+ '20': 'KS',
91328
+ '21': 'KY',
91329
+ '22': 'LA',
91330
+ '23': 'ME',
91331
+ '24': 'MD',
91332
+ '25': 'MA',
91333
+ '26': 'MI',
91334
+ '27': 'MN',
91335
+ '28': 'MS',
91336
+ '29': 'MO',
91337
+ '30': 'MT',
91338
+ '31': 'NE',
91339
+ '32': 'NV',
91340
+ '33': 'NH',
91341
+ '34': 'NJ',
91342
+ '35': 'NM',
91343
+ '36': 'NY',
91344
+ '37': 'NC',
91345
+ '38': 'ND',
91346
+ '39': 'OH',
91347
+ '40': 'OK',
91348
+ '41': 'OR',
91349
+ '42': 'PA',
91350
+ '44': 'RI',
91351
+ '45': 'SC',
91352
+ '46': 'SD',
91353
+ '47': 'TN',
91354
+ '48': 'TX',
91355
+ '49': 'UT',
91356
+ '50': 'VT',
91357
+ '51': 'VA',
91358
+ '53': 'WA',
91359
+ '54': 'WV',
91360
+ '55': 'WI',
91361
+ '56': 'WY',
91362
+ };
91363
+ exports.StateToGEOID = {
91364
+ 'AL': '01',
91365
+ 'AK': '02',
91366
+ 'AZ': '04',
91367
+ 'AR': '05',
91368
+ 'CA': '06',
91369
+ 'CO': '08',
91370
+ 'CT': '09',
91371
+ 'DE': '10',
91372
+ 'FL': '12',
91373
+ 'GA': '13',
91374
+ 'HI': '15',
91375
+ 'ID': '16',
91376
+ 'IL': '17',
91377
+ 'IN': '18',
91378
+ 'IA': '19',
91379
+ 'KS': '20',
91380
+ 'KY': '21',
91381
+ 'LA': '22',
91382
+ 'ME': '23',
91383
+ 'MD': '24',
91384
+ 'MA': '25',
91385
+ 'MI': '26',
91386
+ 'MN': '27',
91387
+ 'MS': '28',
91388
+ 'MO': '29',
91389
+ 'MT': '30',
91390
+ 'NE': '31',
91391
+ 'NV': '32',
91392
+ 'NH': '33',
91393
+ 'NJ': '34',
91394
+ 'NM': '35',
91395
+ 'NY': '36',
91396
+ 'NC': '37',
91397
+ 'ND': '38',
91398
+ 'OH': '39',
91399
+ 'OK': '40',
91400
+ 'OR': '41',
91401
+ 'PA': '42',
91402
+ 'RI': '44',
91403
+ 'SC': '45',
91404
+ 'SD': '46',
91405
+ 'TN': '47',
91406
+ 'TX': '48',
91407
+ 'UT': '49',
91408
+ 'VT': '50',
91409
+ 'VA': '51',
91410
+ 'WA': '53',
91411
+ 'WV': '54',
91412
+ 'WI': '55',
91413
+ 'WY': '56',
91414
+ };
91415
+ function geoidToState(geoid) {
91416
+ let re = /^(..).*$/;
91417
+ let a = re.exec(geoid);
91418
+ if (a == null || a.length != 2)
91419
+ return null;
91420
+ return exports.GEOIDToState[a[1]];
91421
+ }
91422
+ exports.geoidToState = geoidToState;
91423
+
91424
+
91425
+ /***/ }),
91426
+
91427
+ /***/ "@dra2020/util":
91428
+ /*!********************************!*\
91429
+ !*** external "@dra2020/util" ***!
91430
+ \********************************/
91431
+ /*! no static exports found */
91432
+ /***/ (function(module, exports) {
91433
+
91434
+ module.exports = __webpack_require__(/*! @dra2020/util */ "./node_modules/@dra2020/dra-types/node_modules/@dra2020/util/dist/util.js");
91435
+
91436
+ /***/ }),
91437
+
91438
+ /***/ "object-hash":
91439
+ /*!******************************!*\
91440
+ !*** external "object-hash" ***!
91441
+ \******************************/
91442
+ /*! no static exports found */
91443
+ /***/ (function(module, exports) {
91444
+
91445
+ module.exports = __webpack_require__(/*! object-hash */ "./node_modules/@dra2020/dra-types/node_modules/object-hash/index.js");
91446
+
91447
+ /***/ })
91448
+
91449
+ /******/ });
91450
+ });
91451
+
91452
+
91453
+ /***/ }),
91454
+
91455
+ /***/ "./node_modules/@dra2020/dra-types/node_modules/@dra2020/util/dist/util.js":
91456
+ /*!*********************************************************************************!*\
91457
+ !*** ./node_modules/@dra2020/dra-types/node_modules/@dra2020/util/dist/util.js ***!
91458
+ \*********************************************************************************/
91459
+ /*! no static exports found */
91460
+ /***/ (function(module, exports, __webpack_require__) {
91461
+
91462
+ (function webpackUniversalModuleDefinition(root, factory) {
91463
+ if(true)
91464
+ module.exports = factory();
91465
+ else {}
91466
+ })(global, function() {
91467
+ return /******/ (function(modules) { // webpackBootstrap
91468
+ /******/ // The module cache
91469
+ /******/ var installedModules = {};
91470
+ /******/
91471
+ /******/ // The require function
91472
+ /******/ function __webpack_require__(moduleId) {
91473
+ /******/
91474
+ /******/ // Check if module is in cache
91475
+ /******/ if(installedModules[moduleId]) {
91476
+ /******/ return installedModules[moduleId].exports;
91477
+ /******/ }
91478
+ /******/ // Create a new module (and put it into the cache)
91479
+ /******/ var module = installedModules[moduleId] = {
91480
+ /******/ i: moduleId,
91481
+ /******/ l: false,
91482
+ /******/ exports: {}
91483
+ /******/ };
91484
+ /******/
91485
+ /******/ // Execute the module function
91486
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
91487
+ /******/
91488
+ /******/ // Flag the module as loaded
91489
+ /******/ module.l = true;
91490
+ /******/
91491
+ /******/ // Return the exports of the module
91492
+ /******/ return module.exports;
91493
+ /******/ }
91494
+ /******/
91495
+ /******/
91496
+ /******/ // expose the modules object (__webpack_modules__)
91497
+ /******/ __webpack_require__.m = modules;
91498
+ /******/
91499
+ /******/ // expose the module cache
91500
+ /******/ __webpack_require__.c = installedModules;
91501
+ /******/
91502
+ /******/ // define getter function for harmony exports
91503
+ /******/ __webpack_require__.d = function(exports, name, getter) {
91504
+ /******/ if(!__webpack_require__.o(exports, name)) {
91505
+ /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
91506
+ /******/ }
91507
+ /******/ };
91508
+ /******/
91509
+ /******/ // define __esModule on exports
91510
+ /******/ __webpack_require__.r = function(exports) {
91511
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
91512
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
91513
+ /******/ }
91514
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
91515
+ /******/ };
91516
+ /******/
91517
+ /******/ // create a fake namespace object
91518
+ /******/ // mode & 1: value is a module id, require it
91519
+ /******/ // mode & 2: merge all properties of value into the ns
91520
+ /******/ // mode & 4: return value when already ns object
91521
+ /******/ // mode & 8|1: behave like require
91522
+ /******/ __webpack_require__.t = function(value, mode) {
91523
+ /******/ if(mode & 1) value = __webpack_require__(value);
91524
+ /******/ if(mode & 8) return value;
91525
+ /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
91526
+ /******/ var ns = Object.create(null);
91527
+ /******/ __webpack_require__.r(ns);
91528
+ /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
91529
+ /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
91530
+ /******/ return ns;
91531
+ /******/ };
91532
+ /******/
91533
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
91534
+ /******/ __webpack_require__.n = function(module) {
91535
+ /******/ var getter = module && module.__esModule ?
91536
+ /******/ function getDefault() { return module['default']; } :
91537
+ /******/ function getModuleExports() { return module; };
91538
+ /******/ __webpack_require__.d(getter, 'a', getter);
91539
+ /******/ return getter;
91540
+ /******/ };
91541
+ /******/
91542
+ /******/ // Object.prototype.hasOwnProperty.call
91543
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
91544
+ /******/
91545
+ /******/ // __webpack_public_path__
91546
+ /******/ __webpack_require__.p = "";
91547
+ /******/
91548
+ /******/
91549
+ /******/ // Load entry module and return exports
91550
+ /******/ return __webpack_require__(__webpack_require__.s = "./lib/all.ts");
91551
+ /******/ })
91552
+ /************************************************************************/
91553
+ /******/ ({
91554
+
91555
+ /***/ "./lib/all.ts":
91556
+ /*!********************!*\
91557
+ !*** ./lib/all.ts ***!
91558
+ \********************/
91559
+ /*! no static exports found */
91560
+ /***/ (function(module, exports, __webpack_require__) {
91561
+
91562
+ "use strict";
91563
+
91564
+ function __export(m) {
91565
+ for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
91566
+ }
91567
+ Object.defineProperty(exports, "__esModule", { value: true });
91568
+ __export(__webpack_require__(/*! ./util */ "./lib/util.ts"));
91569
+ __export(__webpack_require__(/*! ./countedhash */ "./lib/countedhash.ts"));
91570
+ __export(__webpack_require__(/*! ./indexedarray */ "./lib/indexedarray.ts"));
91571
+
91572
+
91573
+ /***/ }),
91574
+
91575
+ /***/ "./lib/countedhash.ts":
91576
+ /*!****************************!*\
91577
+ !*** ./lib/countedhash.ts ***!
91578
+ \****************************/
91579
+ /*! no static exports found */
91580
+ /***/ (function(module, exports, __webpack_require__) {
91581
+
91582
+ "use strict";
91583
+
91584
+ Object.defineProperty(exports, "__esModule", { value: true });
91585
+ class CountedHash {
91586
+ constructor() {
91587
+ this.n = 0;
91588
+ this.val = {};
91589
+ }
91590
+ get length() { return this.n; }
91591
+ test(id) {
91592
+ return id != '' && this.val[id] !== undefined;
91593
+ }
91594
+ set(id) {
91595
+ if (id != '' && !this.test(id)) {
91596
+ this.n++;
91597
+ this.val[id] = true;
91598
+ }
91599
+ }
91600
+ clear(id) {
91601
+ if (this.test(id)) {
91602
+ this.n--;
91603
+ delete this.val[id];
91604
+ }
91605
+ }
91606
+ empty() {
91607
+ this.n = 0;
91608
+ this.val = {};
91609
+ }
91610
+ asArray() {
91611
+ let a = [];
91612
+ this.forEach(id => { a.push(id); });
91613
+ return a;
91614
+ }
91615
+ asString() {
91616
+ for (var id in this.val)
91617
+ if (this.val.hasOwnProperty(id))
91618
+ return id;
91619
+ return '';
91620
+ }
91621
+ forEach(f) {
91622
+ for (var id in this.val)
91623
+ if (this.val.hasOwnProperty(id))
91624
+ f(id);
91625
+ }
91626
+ }
91627
+ exports.CountedHash = CountedHash;
91628
+
91629
+
91630
+ /***/ }),
91631
+
91632
+ /***/ "./lib/indexedarray.ts":
91633
+ /*!*****************************!*\
91634
+ !*** ./lib/indexedarray.ts ***!
91635
+ \*****************************/
91636
+ /*! no static exports found */
91637
+ /***/ (function(module, exports, __webpack_require__) {
91638
+
91639
+ "use strict";
91640
+
91641
+ Object.defineProperty(exports, "__esModule", { value: true });
91642
+ class IndexedArray {
91643
+ constructor() {
91644
+ this.o = {};
91645
+ this.a = null;
91646
+ }
91647
+ ensure() {
91648
+ if (this.a === null) {
91649
+ this.a = [];
91650
+ for (let p in this.o)
91651
+ if (this.o.hasOwnProperty(p))
91652
+ this.a.push(p);
91653
+ this.a.sort((a, b) => { a = a.toUpperCase(); b = b.toUpperCase(); return a < b ? -1 : (a > b ? 1 : 0); });
91654
+ }
91655
+ }
91656
+ asArray() {
91657
+ this.ensure();
91658
+ return this.a;
91659
+ }
91660
+ get length() { this.ensure(); return this.a.length; }
91661
+ test(s) {
91662
+ return !!s && this.o[s] !== undefined;
91663
+ }
91664
+ set(s) {
91665
+ if (!!s && !this.test(s)) {
91666
+ this.o[s] = true;
91667
+ this.a = null;
91668
+ }
91669
+ }
91670
+ setAll(a) {
91671
+ if (a && a.length)
91672
+ for (let i = 0; i < a.length; i++)
91673
+ this.set(a[i]);
91674
+ }
91675
+ clear(s) {
91676
+ if (this.test(s)) {
91677
+ delete this.o[s];
91678
+ this.a = null;
91679
+ }
91680
+ }
91681
+ at(i) {
91682
+ this.ensure();
91683
+ if (i < 0 || i >= this.a.length)
91684
+ return undefined;
91685
+ return this.a[i];
91686
+ }
91687
+ empty() {
91688
+ this.o = {};
91689
+ this.a = null;
91690
+ }
91691
+ forEach(f) {
91692
+ for (var s in this.o)
91693
+ if (this.o.hasOwnProperty(s))
91694
+ f(s);
91695
+ }
91696
+ }
91697
+ exports.IndexedArray = IndexedArray;
91698
+
91699
+
91700
+ /***/ }),
91701
+
91702
+ /***/ "./lib/util.ts":
91703
+ /*!*********************!*\
91704
+ !*** ./lib/util.ts ***!
91705
+ \*********************/
91706
+ /*! no static exports found */
91707
+ /***/ (function(module, exports, __webpack_require__) {
91708
+
91709
+ "use strict";
91710
+
91711
+ Object.defineProperty(exports, "__esModule", { value: true });
91712
+ function Now() { return (new Date()).toJSON(); }
91713
+ exports.Now = Now;
91714
+ class Elapsed {
91715
+ constructor(bStart = true) {
91716
+ this.tStart = undefined;
91717
+ this.tDur = undefined;
91718
+ if (bStart)
91719
+ this.start();
91720
+ }
91721
+ start() {
91722
+ if (process && process.hrtime)
91723
+ this.tStart = process.hrtime();
91724
+ else
91725
+ this.tStart = performance.now();
91726
+ if (this.tDur)
91727
+ this.tDur = undefined;
91728
+ }
91729
+ end() {
91730
+ if (this.tStart === undefined)
91731
+ this.start();
91732
+ if (process && process.hrtime)
91733
+ this.tDur = process.hrtime(this.tStart);
91734
+ else
91735
+ this.tDur = performance.now() - this.tStart;
91736
+ }
91737
+ ms() {
91738
+ if (this.tDur === undefined)
91739
+ this.end();
91740
+ if (process && process.hrtime)
91741
+ return Math.round((this.tDur[0] * 1000) + (this.tDur[1] / 1000000));
91742
+ else
91743
+ return this.tDur;
91744
+ }
91745
+ nano() {
91746
+ if (this.tDur === undefined)
91747
+ this.end();
91748
+ if (process && process.hrtime)
91749
+ return (this.tDur[0] * 1000000000) + this.tDur[1];
91750
+ else
91751
+ return this.tDur * 1000000;
91752
+ }
91753
+ }
91754
+ exports.Elapsed = Elapsed;
91755
+ class Deadline {
91756
+ constructor(msDelta) {
91757
+ this.msDelta = msDelta;
91758
+ this.elapsed = new Elapsed();
91759
+ }
91760
+ done() {
91761
+ this.elapsed.end();
91762
+ return this.elapsed.ms() > this.msDelta;
91763
+ }
91764
+ }
91765
+ exports.Deadline = Deadline;
91766
+ function createGuid() {
91767
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
91768
+ var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
91769
+ return v.toString(16);
91770
+ });
91771
+ }
91772
+ exports.createGuid = createGuid;
91773
+ function _sizeof(a, loops) {
91774
+ if (a === null || a === undefined)
91775
+ return 0;
91776
+ switch (typeof a) {
91777
+ default: return 0;
91778
+ case 'number': return 8;
91779
+ case 'boolean': return 4;
91780
+ case 'string': return a.length * 2;
91781
+ case 'object':
91782
+ {
91783
+ if (loops.has(a))
91784
+ return 0;
91785
+ else
91786
+ loops.set(a, true);
91787
+ let t = 0;
91788
+ if (Array.isArray(a)) {
91789
+ for (let i = 0; i < a.length; i++)
91790
+ t += _sizeof(a[i], loops);
91791
+ t += 8; // length
91792
+ }
91793
+ else if (Buffer.isBuffer(a)) {
91794
+ t = a.length;
91795
+ }
91796
+ else if (a.hasOwnProperty === undefined)
91797
+ return t;
91798
+ else {
91799
+ for (var key in a)
91800
+ if (a.hasOwnProperty && a.hasOwnProperty(key)) {
91801
+ t += _sizeof(key, loops); // this is a good estimate of download size, but poor estimate of internal size
91802
+ // because of JS object templating vs. naive hashtables
91803
+ t += _sizeof(a[key], loops);
91804
+ }
91805
+ }
91806
+ return t;
91807
+ }
91808
+ }
91809
+ }
91810
+ function sizeof(a) {
91811
+ let loops = new WeakMap();
91812
+ let n = _sizeof(a, loops);
91813
+ return n;
91814
+ }
91815
+ exports.sizeof = sizeof;
91816
+ function depthof(a) {
91817
+ if (a === null || a === undefined)
91818
+ return 1;
91819
+ switch (typeof a) {
91820
+ default: return 1;
91821
+ case 'number': return 1;
91822
+ case 'boolean': return 1;
91823
+ case 'string': return 1;
91824
+ case 'object':
91825
+ {
91826
+ let d = 0;
91827
+ if (Array.isArray(a))
91828
+ return a.length > 0 ? (1 + depthof(a[0])) : 2; // still return 2 for empty array
91829
+ else if (Buffer.isBuffer(a))
91830
+ return 2;
91831
+ else if (a.hasOwnProperty === undefined)
91832
+ return 1;
91833
+ else {
91834
+ for (var key in a)
91835
+ if (a.hasOwnProperty(key))
91836
+ return 1 + depthof(a[key]);
91837
+ return 2; // or 2 for empty object
91838
+ }
91839
+ }
91840
+ }
91841
+ }
91842
+ exports.depthof = depthof;
91843
+ function isEmpty(o) {
91844
+ if (o === null || o === undefined)
91845
+ return true;
91846
+ for (var p in o)
91847
+ if (o.hasOwnProperty(p))
91848
+ return false;
91849
+ return true;
91850
+ }
91851
+ exports.isEmpty = isEmpty;
91852
+ function countKeys(o) {
91853
+ if (o === undefined || typeof o !== 'object')
91854
+ return -1;
91855
+ let count = 0;
91856
+ for (let p in o)
91857
+ if (o.hasOwnProperty(p))
91858
+ count++;
91859
+ return count;
91860
+ }
91861
+ exports.countKeys = countKeys;
91862
+ function nthProperty(o, n = 0) {
91863
+ for (let p in o)
91864
+ if (o.hasOwnProperty(p)) {
91865
+ if (n <= 0)
91866
+ return o[p];
91867
+ n--;
91868
+ }
91869
+ return undefined;
91870
+ }
91871
+ exports.nthProperty = nthProperty;
91872
+ function nthKey(o, n = 0) {
91873
+ for (let p in o)
91874
+ if (o.hasOwnProperty(p)) {
91875
+ if (n <= 0)
91876
+ return p;
91877
+ n--;
91878
+ }
91879
+ return undefined;
91880
+ }
91881
+ exports.nthKey = nthKey;
91882
+ function partialEqual(o, subset) {
91883
+ for (let p in subset)
91884
+ if (subset.hasOwnProperty(p))
91885
+ if (o[p] !== subset[p])
91886
+ return false;
91887
+ return true;
91888
+ }
91889
+ exports.partialEqual = partialEqual;
91890
+ function deepEqual(o1, o2, options) {
91891
+ if (typeof o1 !== typeof o2)
91892
+ return false;
91893
+ if (typeof o1 !== 'object')
91894
+ return o1 === o2;
91895
+ // Special case array
91896
+ if (Array.isArray(o1)) {
91897
+ if (!Array.isArray(o2))
91898
+ return false;
91899
+ if (o1.length != o2.length)
91900
+ return false;
91901
+ if (options && options.unorderedArrays) {
91902
+ o1 = o1.sort();
91903
+ o2 = o2.sort();
91904
+ }
91905
+ for (let i = 0; i < o1.length; i++)
91906
+ if (!deepEqual(o1[i], o2[i], options))
91907
+ return false;
91908
+ return true;
91909
+ }
91910
+ // Special case object
91911
+ if (o1.hasOwnProperty === undefined || o2.hasOwnProperty === undefined)
91912
+ return o1 === o2;
91913
+ for (let p in o1)
91914
+ if (o1.hasOwnProperty(p)) {
91915
+ if (options && options.omitKey[p])
91916
+ continue;
91917
+ if (o2[p] === undefined)
91918
+ return false;
91919
+ if (!deepEqual(o1[p], o2[p], options))
91920
+ return false;
91921
+ }
91922
+ // If any properties in o2 aren't in o1, not equal
91923
+ for (let p in o2)
91924
+ if (o2.hasOwnProperty(p)) {
91925
+ if (options && options.omitKey[p])
91926
+ continue;
91927
+ if (o1[p] === undefined)
91928
+ return false;
91929
+ }
91930
+ return true;
91931
+ }
91932
+ exports.deepEqual = deepEqual;
91933
+ const Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
91934
+ function prettyDate(d) {
91935
+ if (d == null)
91936
+ return 'unknown';
91937
+ let mmm = Months[d.getMonth()];
91938
+ let dd = d.getDate();
91939
+ let yyyy = d.getFullYear();
91940
+ let hh = d.getHours();
91941
+ let m = d.getMinutes();
91942
+ let mm = m < 10 ? `0${m}` : String(m);
91943
+ let ampm = hh >= 12 ? 'PM' : 'AM';
91944
+ if (hh > 12)
91945
+ hh -= 12;
91946
+ return `${mmm} ${dd}, ${yyyy} at ${hh}:${mm} ${ampm}`;
91947
+ }
91948
+ exports.prettyDate = prettyDate;
91949
+ function relativeDate(d) {
91950
+ if (d == null)
91951
+ return 'unknown';
91952
+ let now = new Date();
91953
+ let yyyyNow = now.getFullYear();
91954
+ let mmmNow = Months[now.getMonth()];
91955
+ let ddNow = now.getDate();
91956
+ let mmm = Months[d.getMonth()];
91957
+ let dd = d.getDate();
91958
+ let yyyy = d.getFullYear();
91959
+ let hh = d.getHours();
91960
+ let m = d.getMinutes();
91961
+ let mm = m < 10 ? `0${m}` : String(m);
91962
+ let ampm = hh >= 12 ? 'PM' : 'AM';
91963
+ if (hh > 12)
91964
+ hh -= 12;
91965
+ if (yyyyNow === yyyy && mmmNow === mmm && ddNow === dd)
91966
+ return `Today at ${hh}:${mm} ${ampm}`;
91967
+ else if (yyyyNow === yyyy)
91968
+ return `${mmm} ${dd}`;
91969
+ else
91970
+ return `${mmm} ${dd}, ${yyyy}`;
91971
+ }
91972
+ exports.relativeDate = relativeDate;
91973
+ const OneMinute = 1000 * 60;
91974
+ const OneHour = OneMinute * 60;
91975
+ const OneDay = OneHour * 24;
91976
+ function recentDate(d) {
91977
+ if (d == null)
91978
+ return 'u';
91979
+ let now = new Date();
91980
+ let msNow = now.getTime();
91981
+ let msThen = d.getTime();
91982
+ let msDelta = msNow - msThen;
91983
+ // Within the hour, display in minutes
91984
+ if (msDelta < OneHour)
91985
+ return `${Math.round(msDelta / OneMinute) + 1}m`;
91986
+ // Within the day, display in hours
91987
+ else if (msDelta < OneDay)
91988
+ return `${Math.round(msDelta / OneHour) + 1}h`;
91989
+ // Otherwise, display using relativeDate
91990
+ else
91991
+ return relativeDate(d);
91992
+ }
91993
+ exports.recentDate = recentDate;
91994
+ function textToHtml(sText) {
91995
+ let lines = sText.split('\n');
91996
+ let aHtml = [];
91997
+ let inTable = false;
91998
+ aHtml.push('<body>');
91999
+ for (let i = 0; i < lines.length; i++) {
92000
+ let line = lines[i];
92001
+ let isRow = line.indexOf('|') === 0;
92002
+ if (inTable && !isRow) {
92003
+ aHtml.push('</tbody></table>');
92004
+ inTable = false;
92005
+ }
92006
+ if (isRow && !inTable) {
92007
+ inTable = true;
92008
+ aHtml.push('<table border="1" cellspacing="0" cellpadding="2"><tbody>');
92009
+ }
92010
+ if (isRow) {
92011
+ let cells = line.split('|');
92012
+ if (cells.length > 2) {
92013
+ aHtml.push('<tr>');
92014
+ for (let j = 1; j < cells.length - 1; j++)
92015
+ aHtml.push(`<td>${cells[j]}</td>`);
92016
+ aHtml.push('</tr>');
92017
+ }
92018
+ }
92019
+ else
92020
+ aHtml.push(`<div>${line}&nbsp;</div>`);
92021
+ }
92022
+ if (inTable)
92023
+ aHtml.push('</tbody></table>');
92024
+ aHtml.push('</body>');
92025
+ return aHtml.join('');
92026
+ }
92027
+ exports.textToHtml = textToHtml;
92028
+ function shallowCopy(src) {
92029
+ if (src === null || src === undefined)
92030
+ return src;
92031
+ switch (typeof src) {
92032
+ case 'boolean':
92033
+ case 'number':
92034
+ case 'string':
92035
+ case 'symbol':
92036
+ case 'function':
92037
+ default:
92038
+ return src;
92039
+ case 'object':
92040
+ if (Array.isArray(src))
92041
+ return src.slice();
92042
+ else {
92043
+ let copy = {};
92044
+ for (var p in src)
92045
+ if (src.hasOwnProperty(p))
92046
+ copy[p] = src[p];
92047
+ return copy;
92048
+ }
92049
+ }
92050
+ }
92051
+ exports.shallowCopy = shallowCopy;
92052
+ function shallowAssign(o1, o2) {
92053
+ if (o1 === null || o1 === undefined)
92054
+ o1 = {};
92055
+ if (o2 === null || o2 === undefined)
92056
+ return o1;
92057
+ if (typeof o2 !== 'object' || typeof o1 !== 'object')
92058
+ return o1;
92059
+ for (var p in o2)
92060
+ if (o2.hasOwnProperty(p))
92061
+ o1[p] = o2[p];
92062
+ return o1;
92063
+ }
92064
+ exports.shallowAssign = shallowAssign;
92065
+ function shallowDelete(o1, o2) {
92066
+ if (o1 == null || o2 == null)
92067
+ return o1;
92068
+ if (typeof o2 !== 'object' || typeof o1 !== 'object')
92069
+ return o1;
92070
+ for (var p in o2)
92071
+ if (o2.hasOwnProperty(p))
92072
+ delete o1[p];
92073
+ return o1;
92074
+ }
92075
+ exports.shallowDelete = shallowDelete;
92076
+ function shallowAssignImmutable(o1, o2) {
92077
+ if (o1 === null || o1 === undefined)
92078
+ o1 = {};
92079
+ if (o2 === null || o2 === undefined)
92080
+ return o1;
92081
+ if (typeof o2 !== 'object' || typeof o1 !== 'object')
92082
+ return o1;
92083
+ // First determine whether o2 changes any properties, if it has, make new instance
92084
+ let oNew = o1;
92085
+ for (let p in o2)
92086
+ if (o2.hasOwnProperty(p)) {
92087
+ if (o1[p] != o2[p]) {
92088
+ oNew = shallowCopy(o1);
92089
+ break;
92090
+ }
92091
+ }
92092
+ if (oNew !== o1)
92093
+ shallowAssign(oNew, o2);
92094
+ return oNew;
92095
+ }
92096
+ exports.shallowAssignImmutable = shallowAssignImmutable;
92097
+ function shallowEqual(o1, o2) {
92098
+ if (o1 === undefined || o2 === undefined || typeof o1 !== 'object' || typeof o2 !== 'object')
92099
+ return o1 === o2;
92100
+ if (Array.isArray(o1) && Array.isArray(o2)) {
92101
+ if (o1.length != o2.length)
92102
+ return false;
92103
+ for (let i = 0; i < o1.length; i++)
92104
+ if (o1[i] !== o2[i])
92105
+ return false;
92106
+ return true;
92107
+ }
92108
+ else {
92109
+ let p;
92110
+ for (p in o1)
92111
+ if (o1.hasOwnProperty(p))
92112
+ if (o1[p] !== o2[p])
92113
+ return false;
92114
+ for (p in o2)
92115
+ if (o2.hasOwnProperty(p))
92116
+ if (o1[p] === undefined)
92117
+ return false;
92118
+ return true;
92119
+ }
92120
+ }
92121
+ exports.shallowEqual = shallowEqual;
92122
+ function deepCopy(src) {
92123
+ // Beware typeof oddities
92124
+ if (src === null || src === undefined)
92125
+ return src;
92126
+ if (typeof src === 'object') {
92127
+ if (Array.isArray(src)) {
92128
+ let dst = [];
92129
+ for (let i = 0; i < src.length; i++)
92130
+ dst.push(deepCopy(src[i]));
92131
+ return dst;
92132
+ }
92133
+ else {
92134
+ if (src.hasOwnProperty === undefined)
92135
+ return src;
92136
+ let dst = {};
92137
+ for (var p in src)
92138
+ if (src.hasOwnProperty(p))
92139
+ dst[p] = deepCopy(src[p]);
92140
+ return dst;
92141
+ }
92142
+ }
92143
+ else
92144
+ return src;
92145
+ }
92146
+ exports.deepCopy = deepCopy;
92147
+ function deepAccum(accum, o) {
92148
+ if (accum == null)
92149
+ accum = {};
92150
+ if (o == null)
92151
+ return accum;
92152
+ for (let p in o)
92153
+ if (o.hasOwnProperty(p)) {
92154
+ let vs = o[p];
92155
+ let vd = accum[p];
92156
+ if (typeof vs === 'number') {
92157
+ if (vd !== undefined && typeof vd !== 'number')
92158
+ throw 'deepAccum: unexpected type mismatch';
92159
+ accum[p] = (vd === undefined ? 0 : vd) + vs;
92160
+ }
92161
+ else if (typeof vs === 'object') {
92162
+ if (vd === undefined) {
92163
+ vd = {};
92164
+ accum[p] = vd;
92165
+ }
92166
+ else if (typeof vd !== 'object')
92167
+ throw 'deepAccum: unexpected type mismatch';
92168
+ deepAccum(vd, vs);
92169
+ }
92170
+ }
92171
+ }
92172
+ exports.deepAccum = deepAccum;
92173
+ function precisionRound(n, p) {
92174
+ let f = Math.pow(10, p);
92175
+ return Math.round(n * f) / f;
92176
+ }
92177
+ exports.precisionRound = precisionRound;
92178
+ function percentString(num, den, precision = 0) {
92179
+ if (den == 0)
92180
+ return '(-)';
92181
+ let p = precisionRound((num / den) * 100, precision);
92182
+ return String(p) + '%';
92183
+ }
92184
+ exports.percentString = percentString;
92185
+ function hash(s) {
92186
+ let hash = 5381;
92187
+ let i = s.length;
92188
+ while (i)
92189
+ hash = (hash * 33) ^ s.charCodeAt(--i);
92190
+ /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
92191
+ * integers. Since we want the results to be always positive, convert the
92192
+ * signed int to an unsigned by doing an unsigned bitshift. */
92193
+ return hash >>> 0;
92194
+ }
92195
+ exports.hash = hash;
92196
+ function hashObject(o) {
92197
+ return hash(o ? JSON.stringify(o) : '');
92198
+ }
92199
+ exports.hashObject = hashObject;
92200
+ const HexTable = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
92201
+ function toHex(n) {
92202
+ if (n < 0 || n > 255)
92203
+ throw ('only 0 to 255 supported now');
92204
+ n = Math.floor(n);
92205
+ return HexTable[n >> 4] + HexTable[n & 15];
92206
+ }
92207
+ exports.toHex = toHex;
92208
+ function toRGBA(color, alpha) {
92209
+ let r;
92210
+ let g;
92211
+ let b;
92212
+ switch (color) {
92213
+ case 'white':
92214
+ r = 255;
92215
+ g = 255;
92216
+ b = 255;
92217
+ break;
92218
+ case 'black':
92219
+ r = 0;
92220
+ g = 0;
92221
+ b = 0;
92222
+ break;
92223
+ default:
92224
+ r = parseInt(color.substr(1, 2), 16);
92225
+ g = parseInt(color.substr(3, 2), 16);
92226
+ b = parseInt(color.substr(5, 2), 16);
92227
+ break;
92228
+ }
92229
+ return `rgba(${String(r)}, ${String(g)}, ${String(b)}, ${String(alpha)})`;
92230
+ }
92231
+ exports.toRGBA = toRGBA;
92232
+ function toRGBAIntensity(color, intensity, alpha) {
92233
+ // for now assume color is black
92234
+ let g = precisionRound(255 * intensity, 0);
92235
+ return `rgba(${String(g)}, ${String(g)}, ${String(g)}, ${String(alpha)})`;
92236
+ }
92237
+ exports.toRGBAIntensity = toRGBAIntensity;
92238
+ // Geo functions
92239
+ function distance(x0, y0, x1, y1) {
92240
+ return Math.hypot(x0 - x1, y0 - y1);
92241
+ }
92242
+ exports.distance = distance;
92243
+ function deg2rad(num) { return num * Math.PI / 180; }
92244
+ exports.deg2rad = deg2rad;
92245
+ function rad2deg(num) { return num / Math.PI * 180; }
92246
+ exports.rad2deg = rad2deg;
92247
+ // Restricts lon to range [-180..180]
92248
+ function wrapLon(lon) {
92249
+ let worlds = Math.floor((lon + 180) / 360);
92250
+ return lon - (worlds * 360);
92251
+ }
92252
+ exports.wrapLon = wrapLon;
92253
+
92254
+
92255
+ /***/ })
92256
+
92257
+ /******/ });
92258
+ });
92259
+
92260
+
92261
+ /***/ }),
92262
+
92263
+ /***/ "./node_modules/@dra2020/dra-types/node_modules/object-hash/index.js":
92264
+ /*!***************************************************************************!*\
92265
+ !*** ./node_modules/@dra2020/dra-types/node_modules/object-hash/index.js ***!
92266
+ \***************************************************************************/
92267
+ /*! no static exports found */
92268
+ /***/ (function(module, exports, __webpack_require__) {
92269
+
92270
+ "use strict";
92271
+
92272
+
92273
+ var crypto = __webpack_require__(/*! crypto */ "crypto");
92274
+
92275
+ /**
92276
+ * Exported function
92277
+ *
92278
+ * Options:
92279
+ *
92280
+ * - `algorithm` hash algo to be used by this instance: *'sha1', 'md5'
92281
+ * - `excludeValues` {true|*false} hash object keys, values ignored
92282
+ * - `encoding` hash encoding, supports 'buffer', '*hex', 'binary', 'base64'
92283
+ * - `ignoreUnknown` {true|*false} ignore unknown object types
92284
+ * - `replacer` optional function that replaces values before hashing
92285
+ * - `respectFunctionProperties` {*true|false} consider function properties when hashing
92286
+ * - `respectFunctionNames` {*true|false} consider 'name' property of functions for hashing
92287
+ * - `respectType` {*true|false} Respect special properties (prototype, constructor)
92288
+ * when hashing to distinguish between types
92289
+ * - `unorderedArrays` {true|*false} Sort all arrays before hashing
92290
+ * - `unorderedSets` {*true|false} Sort `Set` and `Map` instances before hashing
92291
+ * * = default
92292
+ *
92293
+ * @param {object} object value to hash
92294
+ * @param {object} options hashing options
92295
+ * @return {string} hash value
92296
+ * @api public
92297
+ */
92298
+ exports = module.exports = objectHash;
92299
+
92300
+ function objectHash(object, options){
92301
+ options = applyDefaults(object, options);
92302
+
92303
+ return hash(object, options);
92304
+ }
92305
+
92306
+ /**
92307
+ * Exported sugar methods
92308
+ *
92309
+ * @param {object} object value to hash
92310
+ * @return {string} hash value
92311
+ * @api public
92312
+ */
92313
+ exports.sha1 = function(object){
92314
+ return objectHash(object);
92315
+ };
92316
+ exports.keys = function(object){
92317
+ return objectHash(object, {excludeValues: true, algorithm: 'sha1', encoding: 'hex'});
92318
+ };
92319
+ exports.MD5 = function(object){
92320
+ return objectHash(object, {algorithm: 'md5', encoding: 'hex'});
92321
+ };
92322
+ exports.keysMD5 = function(object){
92323
+ return objectHash(object, {algorithm: 'md5', encoding: 'hex', excludeValues: true});
92324
+ };
92325
+
92326
+ // Internals
92327
+ var hashes = crypto.getHashes ? crypto.getHashes().slice() : ['sha1', 'md5'];
92328
+ hashes.push('passthrough');
92329
+ var encodings = ['buffer', 'hex', 'binary', 'base64'];
92330
+
92331
+ function applyDefaults(object, sourceOptions){
92332
+ sourceOptions = sourceOptions || {};
92333
+
92334
+ // create a copy rather than mutating
92335
+ var options = {};
92336
+ options.algorithm = sourceOptions.algorithm || 'sha1';
92337
+ options.encoding = sourceOptions.encoding || 'hex';
92338
+ options.excludeValues = sourceOptions.excludeValues ? true : false;
92339
+ options.algorithm = options.algorithm.toLowerCase();
92340
+ options.encoding = options.encoding.toLowerCase();
92341
+ options.ignoreUnknown = sourceOptions.ignoreUnknown !== true ? false : true; // default to false
92342
+ options.respectType = sourceOptions.respectType === false ? false : true; // default to true
92343
+ options.respectFunctionNames = sourceOptions.respectFunctionNames === false ? false : true;
92344
+ options.respectFunctionProperties = sourceOptions.respectFunctionProperties === false ? false : true;
92345
+ options.unorderedArrays = sourceOptions.unorderedArrays !== true ? false : true; // default to false
92346
+ options.unorderedSets = sourceOptions.unorderedSets === false ? false : true; // default to false
92347
+ options.unorderedObjects = sourceOptions.unorderedObjects === false ? false : true; // default to true
92348
+ options.replacer = sourceOptions.replacer || undefined;
92349
+ options.excludeKeys = sourceOptions.excludeKeys || undefined;
92350
+
92351
+ if(typeof object === 'undefined') {
92352
+ throw new Error('Object argument required.');
92353
+ }
92354
+
92355
+ // if there is a case-insensitive match in the hashes list, accept it
92356
+ // (i.e. SHA256 for sha256)
92357
+ for (var i = 0; i < hashes.length; ++i) {
92358
+ if (hashes[i].toLowerCase() === options.algorithm.toLowerCase()) {
92359
+ options.algorithm = hashes[i];
92360
+ }
92361
+ }
92362
+
92363
+ if(hashes.indexOf(options.algorithm) === -1){
92364
+ throw new Error('Algorithm "' + options.algorithm + '" not supported. ' +
92365
+ 'supported values: ' + hashes.join(', '));
92366
+ }
92367
+
92368
+ if(encodings.indexOf(options.encoding) === -1 &&
92369
+ options.algorithm !== 'passthrough'){
92370
+ throw new Error('Encoding "' + options.encoding + '" not supported. ' +
92371
+ 'supported values: ' + encodings.join(', '));
92372
+ }
92373
+
92374
+ return options;
92375
+ }
92376
+
92377
+ /** Check if the given function is a native function */
92378
+ function isNativeFunction(f) {
92379
+ if ((typeof f) !== 'function') {
92380
+ return false;
92381
+ }
92382
+ var exp = /^function\s+\w*\s*\(\s*\)\s*{\s+\[native code\]\s+}$/i;
92383
+ return exp.exec(Function.prototype.toString.call(f)) != null;
92384
+ }
92385
+
92386
+ function hash(object, options) {
92387
+ var hashingStream;
92388
+
92389
+ if (options.algorithm !== 'passthrough') {
92390
+ hashingStream = crypto.createHash(options.algorithm);
92391
+ } else {
92392
+ hashingStream = new PassThrough();
92393
+ }
92394
+
92395
+ if (typeof hashingStream.write === 'undefined') {
92396
+ hashingStream.write = hashingStream.update;
92397
+ hashingStream.end = hashingStream.update;
92398
+ }
92399
+
92400
+ var hasher = typeHasher(options, hashingStream);
92401
+ hasher.dispatch(object);
92402
+ if (!hashingStream.update) {
92403
+ hashingStream.end('');
92404
+ }
92405
+
92406
+ if (hashingStream.digest) {
92407
+ return hashingStream.digest(options.encoding === 'buffer' ? undefined : options.encoding);
92408
+ }
92409
+
92410
+ var buf = hashingStream.read();
92411
+ if (options.encoding === 'buffer') {
92412
+ return buf;
92413
+ }
92414
+
92415
+ return buf.toString(options.encoding);
92416
+ }
90607
92417
 
92418
+ /**
92419
+ * Expose streaming API
92420
+ *
92421
+ * @param {object} object Value to serialize
92422
+ * @param {object} options Options, as for hash()
92423
+ * @param {object} stream A stream to write the serializiation to
92424
+ * @api public
92425
+ */
92426
+ exports.writeToStream = function(object, options, stream) {
92427
+ if (typeof stream === 'undefined') {
92428
+ stream = options;
92429
+ options = {};
92430
+ }
90608
92431
 
90609
- /***/ }),
92432
+ options = applyDefaults(object, options);
90610
92433
 
90611
- /***/ "./testdata/profiles/sample-profile.json":
90612
- /*!***********************************************!*\
90613
- !*** ./testdata/profiles/sample-profile.json ***!
90614
- \***********************************************/
90615
- /*! exports provided: state, planName, nDistricts, nCounties, legislativeDistricts, populationProfile, compactnessProfile, splittingProfile, partisanProfile, demographicProfile, default */
90616
- /***/ (function(module) {
92434
+ return typeHasher(options, stream).dispatch(object);
92435
+ };
90617
92436
 
90618
- module.exports = JSON.parse("{\"state\":\"NC\",\"planName\":\"Sample profile\",\"nDistricts\":13,\"nCounties\":100,\"legislativeDistricts\":false,\"populationProfile\":{\"TotalPopByDistrict\":[725807,721754,732627,733218,730051,729580,728476,729721,731507,724085,733447,730845,729710],\"targetSize\":729294},\"compactnessProfile\":{\"GeometryByDistrict\":[[1.5773977911324972,10.187637000340999,2.6574300583946115],[0.7058475358440041,7.6543777040386445,1.7632772181381422],[2.920140664739499,9.983421957097065,2.733997011824872],[0.19139679136850038,3.5473804553379114,0.9224411598627837],[1.0389865962844946,6.505473449741405,2.1061393959539023],[1.0334553158144995,6.472595447414713,1.6394147493749138],[1.6241801669130118,8.040301088673953,1.952917034583555],[0.7706765524270006,7.015983900119356,2.1886348192462464],[0.9976965229710011,8.405282685068,2.5604335810665138],[0.676265120610002,5.866361656441843,1.7264712385837822],[1.719226531340005,10.410671767954964,3.1586859214584764],[0.11329460131899982,2.3038812649606695,0.5379072920877442],[0.48373856130249987,5.267369326010034,1.45173753753012]]},\"splittingProfile\":{\"CountyPopByDistrict\":[[0,0,0,0,0,0,0,0,21282,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240299,56552,0,0,0,12197,0,59916,0,0,54691,0,0,0,24669,0,0,0,0,0,0,0,0,0,0,0,0,24505,0,0,0,0,0,0,22099,0,0,0,0,0,0,0,74235,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45422,0,20972,13228,0,0,0,55740,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60619,0,0,0,0,0,0,0,114678,0,0,0,0,0,0,0,109245,0,0,0,0,0,0,0,0,0,0,0,0,95840,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,324879,0,0,0,0,0,16493,0,0],[0,0,0,0,0,0,0,47759,0,0,0,0,0,0,0,9980,66469,0,0,0,0,14793,0,0,0,103505,0,23547,33920,0,0,0,0,0,0,0,0,0,0,0,21362,0,0,0,0,0,0,0,5810,0,0,0,10153,0,59495,0,0,0,0,0,0,0,0,0,0,0,0,177772,0,13144,40661,0,13453,0,86397,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4407,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27288,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133801,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,572129,0,0,0,0,0,0,0,0],[0,0,37198,11155,0,27281,17797,0,0,0,0,0,0,0,0,0,0,0,6051,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,350670,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47401,73673,0,0,0,0,0,0,0,0,51079,0,69340,0,38406,0],[0,151131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23719,0,63505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158500,0,0,0,0,0,0,0,0,0,0,0,57866,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39464,0,0,141752,0,0,93643,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,8316,107431,0,0,0,0,0,0,0,0,0,0,0,0,0,58098,0,0,0,0,0,0,58505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55188,0,0,0,0,0,0,0,0,0,0,0,0,0,202667,0,0,0,0,0,52217,0,0,0,0,0,0,0,0,0,0,63431,0,0,0,0,0,0,0,0,0,0,0,0,0,122623,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,178011,0,0,0,0,0,0,0,0,0,0,0,0,242290,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46952,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27798,88247,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85838,0,0,0,60585,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,26948,0,0,0,0,25045,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185734,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46639,134168,0,0,0,0,36157,0,0,0,0,0,0,201292,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,108857,0,0,0,0,0,0,144479,0,0,0,0,98078,0,0,0,0,0,0,0,0,0,0,0,0,206086,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78265,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20510,0,0,0,0,0,67810,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,126417,90912,0,83029,0,0,0,0,0,27444,0,10587,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8861,0,0,0,0,0,59036,106740,0,0,0,0,40271,0,0,0,0,0,44996,33922,20764,0,0,15579,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13981,33090,0,0,0,0,0,0,0,0,0,0,0,17818],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,730845,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,162878,41240,0,0,0,0,0,0,0,0,0,0,325932,0,0,0,0,0,0,0,150509,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49151,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]},\"partisanProfile\":{\"statewideVf\":0.515036,\"VfArray\":[0.4235,0.428588,0.43388,0.443866,0.454505,0.456985,0.458005,0.458134,0.463947,0.473144,0.718314,0.73662,0.775817]},\"demographicProfile\":{\"DemographicsByDistrict\":[[0.462,0.538,0.0659,0.4445,0.0219,0.0009,0.013],[0.7063,0.2937,0.0714,0.1968,0.0203,0.001,0.0113],[0.7125,0.2875,0.0537,0.2118,0.0163,0.0021,0.0104],[0.6162,0.3838,0.0869,0.2239,0.0715,0.0012,0.0101],[0.7767,0.2233,0.0669,0.1402,0.013,0.0008,0.0077],[0.7112,0.2888,0.0715,0.1985,0.0136,0.0008,0.0102],[0.7086,0.2914,0.0695,0.2023,0.0097,0.0011,0.0142],[0.6698,0.3302,0.0725,0.224,0.0229,0.0029,0.0188],[0.6406,0.3594,0.0594,0.1962,0.0222,0.001,0.0862],[0.8193,0.1807,0.0451,0.1158,0.0141,0.0007,0.008],[0.8956,0.1044,0.0433,0.0327,0.0103,0.0012,0.0197],[0.4657,0.5343,0.1215,0.3618,0.0507,0.0017,0.011],[0.698,0.302,0.0572,0.2117,0.0286,0.0009,0.0095]]}}");
92437
+ function typeHasher(options, writeTo, context){
92438
+ context = context || [];
92439
+ var write = function(str) {
92440
+ if (writeTo.update) {
92441
+ return writeTo.update(str, 'utf8');
92442
+ } else {
92443
+ return writeTo.write(str, 'utf8');
92444
+ }
92445
+ };
90619
92446
 
90620
- /***/ }),
92447
+ return {
92448
+ dispatch: function(value){
92449
+ if (options.replacer) {
92450
+ value = options.replacer(value);
92451
+ }
90621
92452
 
90622
- /***/ "./testdata/profiles/sample-scorecard.json":
90623
- /*!*************************************************!*\
90624
- !*** ./testdata/profiles/sample-scorecard.json ***!
90625
- \*************************************************/
90626
- /*! exports provided: score, best, fair, default */
90627
- /***/ (function(module) {
92453
+ var type = typeof value;
92454
+ if (value === null) {
92455
+ type = 'null';
92456
+ }
90628
92457
 
90629
- module.exports = JSON.parse("{\"score\":5,\"best\":{\"BestScore\":18,\"compactness\":{\"score\":35,\"reock\":{\"raw\":0.3373,\"normalized\":35,\"notes\":{}},\"polsby\":{\"raw\":0.2418,\"normalized\":35,\"notes\":{}}},\"splitting\":{\"score\":0,\"county\":{\"raw\":1.1474,\"normalized\":0,\"notes\":{}},\"district\":{\"raw\":1.4839,\"normalized\":3,\"notes\":{}}},\"populationDeviation\":{\"raw\":0.016,\"normalized\":0,\"notes\":{\"maxDeviation\":11693}}},\"fair\":{\"bias\":{\"BestS\":7,\"BestSf\":0.5385,\"ProbableS\":4.1925,\"ProbableSf\":0.3225,\"Bias\":0.216,\"normalized\":0,\"notes\":{}},\"competitiveness\":{\"Cd\":0.7266,\"Cdf\":0.0559,\"Mdf\":0.1019,\"normalized\":10,\"notes\":{}},\"score\":2,\"notes\":{}}}");
92458
+ //console.log("[DEBUG] Dispatch: ", value, "->", type, " -> ", "_" + type);
90630
92459
 
90631
- /***/ })
92460
+ return this['_' + type](value);
92461
+ },
92462
+ _object: function(object) {
92463
+ var pattern = (/\[object (.*)\]/i);
92464
+ var objString = Object.prototype.toString.call(object);
92465
+ var objType = pattern.exec(objString);
92466
+ if (!objType) { // object type did not match [object ...]
92467
+ objType = 'unknown:[' + objString + ']';
92468
+ } else {
92469
+ objType = objType[1]; // take only the class name
92470
+ }
90632
92471
 
90633
- /******/ });
90634
- });
92472
+ objType = objType.toLowerCase();
92473
+
92474
+ var objectNumber = null;
92475
+
92476
+ if ((objectNumber = context.indexOf(object)) >= 0) {
92477
+ return this.dispatch('[CIRCULAR:' + objectNumber + ']');
92478
+ } else {
92479
+ context.push(object);
92480
+ }
92481
+
92482
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(object)) {
92483
+ write('buffer:');
92484
+ return write(object);
92485
+ }
92486
+
92487
+ if(objType !== 'object' && objType !== 'function') {
92488
+ if(this['_' + objType]) {
92489
+ this['_' + objType](object);
92490
+ } else if (options.ignoreUnknown) {
92491
+ return write('[' + objType + ']');
92492
+ } else {
92493
+ throw new Error('Unknown object type "' + objType + '"');
92494
+ }
92495
+ }else{
92496
+ var keys = Object.keys(object);
92497
+ if (options.unorderedObjects) {
92498
+ keys = keys.sort();
92499
+ }
92500
+ // Make sure to incorporate special properties, so
92501
+ // Types with different prototypes will produce
92502
+ // a different hash and objects derived from
92503
+ // different functions (`new Foo`, `new Bar`) will
92504
+ // produce different hashes.
92505
+ // We never do this for native functions since some
92506
+ // seem to break because of that.
92507
+ if (options.respectType !== false && !isNativeFunction(object)) {
92508
+ keys.splice(0, 0, 'prototype', '__proto__', 'constructor');
92509
+ }
92510
+
92511
+ if (options.excludeKeys) {
92512
+ keys = keys.filter(function(key) { return !options.excludeKeys(key); });
92513
+ }
92514
+
92515
+ write('object:' + keys.length + ':');
92516
+ var self = this;
92517
+ return keys.forEach(function(key){
92518
+ self.dispatch(key);
92519
+ write(':');
92520
+ if(!options.excludeValues) {
92521
+ self.dispatch(object[key]);
92522
+ }
92523
+ write(',');
92524
+ });
92525
+ }
92526
+ },
92527
+ _array: function(arr, unordered){
92528
+ unordered = typeof unordered !== 'undefined' ? unordered :
92529
+ options.unorderedArrays !== false; // default to options.unorderedArrays
92530
+
92531
+ var self = this;
92532
+ write('array:' + arr.length + ':');
92533
+ if (!unordered || arr.length <= 1) {
92534
+ return arr.forEach(function(entry) {
92535
+ return self.dispatch(entry);
92536
+ });
92537
+ }
92538
+
92539
+ // the unordered case is a little more complicated:
92540
+ // since there is no canonical ordering on objects,
92541
+ // i.e. {a:1} < {a:2} and {a:1} > {a:2} are both false,
92542
+ // we first serialize each entry using a PassThrough stream
92543
+ // before sorting.
92544
+ // also: we can’t use the same context array for all entries
92545
+ // since the order of hashing should *not* matter. instead,
92546
+ // we keep track of the additions to a copy of the context array
92547
+ // and add all of them to the global context array when we’re done
92548
+ var contextAdditions = [];
92549
+ var entries = arr.map(function(entry) {
92550
+ var strm = new PassThrough();
92551
+ var localContext = context.slice(); // make copy
92552
+ var hasher = typeHasher(options, strm, localContext);
92553
+ hasher.dispatch(entry);
92554
+ // take only what was added to localContext and append it to contextAdditions
92555
+ contextAdditions = contextAdditions.concat(localContext.slice(context.length));
92556
+ return strm.read().toString();
92557
+ });
92558
+ context = context.concat(contextAdditions);
92559
+ entries.sort();
92560
+ return this._array(entries, false);
92561
+ },
92562
+ _date: function(date){
92563
+ return write('date:' + date.toJSON());
92564
+ },
92565
+ _symbol: function(sym){
92566
+ return write('symbol:' + sym.toString());
92567
+ },
92568
+ _error: function(err){
92569
+ return write('error:' + err.toString());
92570
+ },
92571
+ _boolean: function(bool){
92572
+ return write('bool:' + bool.toString());
92573
+ },
92574
+ _string: function(string){
92575
+ write('string:' + string.length + ':');
92576
+ write(string.toString());
92577
+ },
92578
+ _function: function(fn){
92579
+ write('fn:');
92580
+ if (isNativeFunction(fn)) {
92581
+ this.dispatch('[native]');
92582
+ } else {
92583
+ this.dispatch(fn.toString());
92584
+ }
92585
+
92586
+ if (options.respectFunctionNames !== false) {
92587
+ // Make sure we can still distinguish native functions
92588
+ // by their name, otherwise String and Function will
92589
+ // have the same hash
92590
+ this.dispatch("function-name:" + String(fn.name));
92591
+ }
92592
+
92593
+ if (options.respectFunctionProperties) {
92594
+ this._object(fn);
92595
+ }
92596
+ },
92597
+ _number: function(number){
92598
+ return write('number:' + number.toString());
92599
+ },
92600
+ _xml: function(xml){
92601
+ return write('xml:' + xml.toString());
92602
+ },
92603
+ _null: function() {
92604
+ return write('Null');
92605
+ },
92606
+ _undefined: function() {
92607
+ return write('Undefined');
92608
+ },
92609
+ _regexp: function(regex){
92610
+ return write('regex:' + regex.toString());
92611
+ },
92612
+ _uint8array: function(arr){
92613
+ write('uint8array:');
92614
+ return this.dispatch(Array.prototype.slice.call(arr));
92615
+ },
92616
+ _uint8clampedarray: function(arr){
92617
+ write('uint8clampedarray:');
92618
+ return this.dispatch(Array.prototype.slice.call(arr));
92619
+ },
92620
+ _int8array: function(arr){
92621
+ write('uint8array:');
92622
+ return this.dispatch(Array.prototype.slice.call(arr));
92623
+ },
92624
+ _uint16array: function(arr){
92625
+ write('uint16array:');
92626
+ return this.dispatch(Array.prototype.slice.call(arr));
92627
+ },
92628
+ _int16array: function(arr){
92629
+ write('uint16array:');
92630
+ return this.dispatch(Array.prototype.slice.call(arr));
92631
+ },
92632
+ _uint32array: function(arr){
92633
+ write('uint32array:');
92634
+ return this.dispatch(Array.prototype.slice.call(arr));
92635
+ },
92636
+ _int32array: function(arr){
92637
+ write('uint32array:');
92638
+ return this.dispatch(Array.prototype.slice.call(arr));
92639
+ },
92640
+ _float32array: function(arr){
92641
+ write('float32array:');
92642
+ return this.dispatch(Array.prototype.slice.call(arr));
92643
+ },
92644
+ _float64array: function(arr){
92645
+ write('float64array:');
92646
+ return this.dispatch(Array.prototype.slice.call(arr));
92647
+ },
92648
+ _arraybuffer: function(arr){
92649
+ write('arraybuffer:');
92650
+ return this.dispatch(new Uint8Array(arr));
92651
+ },
92652
+ _url: function(url) {
92653
+ return write('url:' + url.toString(), 'utf8');
92654
+ },
92655
+ _map: function(map) {
92656
+ write('map:');
92657
+ var arr = Array.from(map);
92658
+ return this._array(arr, options.unorderedSets !== false);
92659
+ },
92660
+ _set: function(set) {
92661
+ write('set:');
92662
+ var arr = Array.from(set);
92663
+ return this._array(arr, options.unorderedSets !== false);
92664
+ },
92665
+ _blob: function() {
92666
+ if (options.ignoreUnknown) {
92667
+ return write('[blob]');
92668
+ }
92669
+
92670
+ throw Error('Hashing Blob objects is currently not supported\n' +
92671
+ '(see https://github.com/puleos/object-hash/issues/26)\n' +
92672
+ 'Use "options.replacer" or "options.ignoreUnknown"\n');
92673
+ },
92674
+ _domwindow: function() { return write('domwindow'); },
92675
+ /* Node.js standard native objects */
92676
+ _process: function() { return write('process'); },
92677
+ _timer: function() { return write('timer'); },
92678
+ _pipe: function() { return write('pipe'); },
92679
+ _tcp: function() { return write('tcp'); },
92680
+ _udp: function() { return write('udp'); },
92681
+ _tty: function() { return write('tty'); },
92682
+ _statwatcher: function() { return write('statwatcher'); },
92683
+ _securecontext: function() { return write('securecontext'); },
92684
+ _connection: function() { return write('connection'); },
92685
+ _zlib: function() { return write('zlib'); },
92686
+ _context: function() { return write('context'); },
92687
+ _nodescript: function() { return write('nodescript'); },
92688
+ _httpparser: function() { return write('httpparser'); },
92689
+ _dataview: function() { return write('dataview'); },
92690
+ _signal: function() { return write('signal'); },
92691
+ _fsevent: function() { return write('fsevent'); },
92692
+ _tlswrap: function() { return write('tlswrap'); }
92693
+ };
92694
+ }
92695
+
92696
+ // Mini-implementation of stream.PassThrough
92697
+ // We are far from having need for the full implementation, and we can
92698
+ // make assumptions like "many writes, then only one final read"
92699
+ // and we can ignore encoding specifics
92700
+ function PassThrough() {
92701
+ return {
92702
+ buf: '',
92703
+
92704
+ write: function(b) {
92705
+ this.buf += b;
92706
+ },
92707
+
92708
+ end: function(b) {
92709
+ this.buf += b;
92710
+ },
92711
+
92712
+ read: function() {
92713
+ return this.buf;
92714
+ }
92715
+ };
92716
+ }
90635
92717
 
90636
92718
 
90637
92719
  /***/ }),
@@ -98601,11 +100683,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
98601
100683
  return result;
98602
100684
  };
98603
100685
  Object.defineProperty(exports, "__esModule", { value: true });
100686
+ const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "./node_modules/@dra2020/dra-score/dist/dra-score.bundle.js"));
98604
100687
  const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
98605
100688
  const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
98606
100689
  const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
98607
100690
  const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
98608
100691
  const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
100692
+ const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
98609
100693
  const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
98610
100694
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
98611
100695
  class AnalyticsSession {
@@ -98625,45 +100709,48 @@ class AnalyticsSession {
98625
100709
  this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
98626
100710
  this.plan = new D.Plan(this, SessionRequest['plan']);
98627
100711
  this.districts = new D.Districts(this, SessionRequest['districtShapes']);
98628
- // TODO - Confirming that the graph includes OUT_OF_STATE neighbors
98629
- // if (U.keyExists(S.OUT_OF_STATE, this.graph._graph)) {
98630
- // console.log("Contiguity graph includes out-of-state neighbors.");
98631
- // }
98632
- // else {
98633
- // console.log("Contiguity graph does NOT include out-of-state neighbors.");
98634
- // }
98635
- // NOTE: I've pulled these out of the individual analytics to here. Eventually,
98636
- // we could want them to passed into an analytics session as data, along with
98637
- // everything else. For now, this keeps branching out of the main code.
98638
- results_1.doConfigureScales(this);
100712
+ // TODO - SCORE: Toggle
100713
+ if (this.useLegacy()) {
100714
+ console.log("Using legacy district-analytics.");
100715
+ // NOTE: I've pulled these out of the individual analytics to here. Eventually,
100716
+ // we could want them to passed into an analytics session as data, along with
100717
+ // everything else. For now, this keeps branching out of the main code.
100718
+ results_1.doConfigureScales(this);
100719
+ }
100720
+ else {
100721
+ console.log("Using dra-score analytics.");
100722
+ // TODO - SCORE: Temporary HACK. Query dra-score for threshold.
100723
+ results_1.doConfigureScales(this);
100724
+ }
98639
100725
  }
98640
100726
  processConfig(config) {
98641
100727
  // NOTE - Session settings are required:
98642
100728
  // - Analytics suites can be defaulted to all with [], but
98643
100729
  // - Dataset keys must be explicitly specified with 'dataset'
98644
- // TODO - DASHBOARD: Remove this mechanism. Always run everything.
98645
- let defaultSuites = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
98646
- // If the config passed in has no suites = [], use the default suites
98647
- if (U.isArrayEmpty(config['suites'])) {
98648
- config['suites'] = defaultSuites;
98649
- }
100730
+ // TODO - SCORE: Delete
100731
+ config['suites'] = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
98650
100732
  // Default the Census & redistricting cycle to 2010
98651
100733
  if (!(U.keyExists('cycle', config)))
98652
100734
  config['cycle'] = 2010;
98653
100735
  return config;
98654
100736
  }
100737
+ // TODO - SCORE: Toggle = Invert this logic
100738
+ useLegacy() {
100739
+ // TODO - SCORE: Opt-out
100740
+ return (U.keyExists('useScore', this.config) && (this.config['useScore'] != null) && (!(this.config['useScore']))) ? true : false;
100741
+ // TODO - SCORE: Opt-in
100742
+ // return (U.keyExists('useScore', this.config) && (this.config['useScore'])) ? false : true;
100743
+ }
98655
100744
  // Using the the data in the analytics session, calculate all the
98656
100745
  // analytics & validations, saving/updating the individual test results.
98657
- analyzePlan(bLog = false) {
100746
+ analyzePlan(bLog = false, overridesJSON) {
98658
100747
  try {
98659
100748
  preprocess_1.doPreprocessData(this, bLog);
98660
100749
  analyze_1.doAnalyzeDistricts(this, bLog);
98661
100750
  analyze_1.doAnalyzePlan(this, bLog);
98662
100751
  // TODO - SCORE
98663
100752
  this._profile = score_1.profilePlan(this, bLog);
98664
- // this._profile = Score.sampleProfile;
98665
- this._scorecard = score_1.scorePlan(this._profile, bLog);
98666
- // TODO - SCORE
100753
+ this._scorecard = score_1.scorePlan(this, this._profile, bLog, overridesJSON);
98667
100754
  results_1.doAnalyzePostProcessing(this, bLog);
98668
100755
  }
98669
100756
  catch (_a) {
@@ -98684,13 +100771,6 @@ class AnalyticsSession {
98684
100771
  getPlanScorecard(bLog = false) {
98685
100772
  return this._scorecard;
98686
100773
  }
98687
- /* TODO - SCORE
98688
- // NOTE - This assumes that analyzePlan() has been run!
98689
- getPlanAnalytics(bLog: boolean = false): PlanAnalytics
98690
- {
98691
- return preparePlanAnalytics(this, bLog);
98692
- }
98693
- */
98694
100774
  // TODO - SCORE
98695
100775
  // NOTE - This assumes that analyzePlan() has been run!
98696
100776
  getRequirementsChecklist(bLog = false) {
@@ -98699,19 +100779,82 @@ class AnalyticsSession {
98699
100779
  // NOTE - This assumes that analyzePlan() has been run!
98700
100780
  getDiscontiguousDistrictFeatures(bLog = false) {
98701
100781
  // Get the (possibly empty) list of discontiguous district IDs
98702
- let contiguousTest = this.getTest(1 /* Contiguous */);
98703
- let discontiguousDistrictIDs = contiguousTest['details']['discontiguousDistricts'] || [];
100782
+ const contiguousTest = this.getTest(1 /* Contiguous */);
100783
+ const discontiguousDistrictIDs = contiguousTest['details']['discontiguousDistricts'] || [];
98704
100784
  // Convert them into a (possibly empty) list of features
98705
100785
  let discontiguousDistrictFeatures = { type: 'FeatureCollection', features: [] };
98706
100786
  if (!(U.isArrayEmpty(discontiguousDistrictIDs))) {
98707
100787
  for (let id of discontiguousDistrictIDs) {
98708
100788
  let poly = this.districts.getDistrictShapeByID(id);
98709
- if (poly)
98710
- discontiguousDistrictFeatures.features.push(poly);
100789
+ if (poly) {
100790
+ // If a district has a shape & it is not contiguous, by definition,
100791
+ // it will be a Multipolygon, i.e., it will have multiple pieces, some
100792
+ // possibly embedded w/in other districts. Get & add all the pieces.
100793
+ const districtParts = geofeature_1.polyParts(poly);
100794
+ discontiguousDistrictFeatures.features.push(...districtParts.features);
100795
+ // discontiguousDistrictFeatures.features.push(poly);
100796
+ }
98711
100797
  }
98712
100798
  }
98713
100799
  return discontiguousDistrictFeatures;
98714
100800
  }
100801
+ // Comments clipped from dra-client geodistrict.ts.
100802
+ // Discontiguous polygons are:
100803
+ // 1. All polygons in a multi-polygon; and
100804
+ // 2. All holes in a otherwise cohesive polygon.
100805
+ // Note that all non-cohesive features are always simple polygons.
100806
+ /*
100807
+ let i: number, j: number;
100808
+ let nPoly: number = 0;
100809
+ for (i = 0;nPoly == 0 && i < this.cacheDistricts.features.length;i++)
100810
+ {
100811
+ let f = this.cacheDistricts.features[i];
100812
+
100813
+ if (f.geometry.type === 'MultiPolygon')
100814
+ nPoly += f.geometry.coordinates.length;
100815
+ else if (f.geometry.type === 'Polygon' && f.geometry.coordinates.length)
100816
+ nPoly += (f.geometry.coordinates.length - 1);
100817
+ }
100818
+ if (nPoly)
100819
+ {
100820
+ this.cacheNoncohesive = {type: 'FeatureCollection', features: []};
100821
+ let af: any = this.cacheNoncohesive.features;
100822
+ let oUnique: any = {};
100823
+
100824
+ // First add discontiguous polygons
100825
+ for (i = 0;i < this.cacheDistricts.features.length;i++)
100826
+ {
100827
+ let f = this.cacheDistricts.features[i];
100828
+
100829
+ if (f.geometry.type === 'MultiPolygon')
100830
+ {
100831
+ // Push all non-contiguous polygons
100832
+ for (j = 0;j < f.geometry.coordinates.length;j++)
100833
+ {
100834
+ let p: any = f.geometry.coordinates[j];
100835
+ oUnique[Hash.qhash(p[0])] = true;
100836
+ af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: p}});
100837
+ }
100838
+ }
100839
+ }
100840
+
100841
+ // Now add unique holes
100842
+ for (i = 0;i < this.cacheDistricts.features.length;i++)
100843
+ {
100844
+ let f = this.cacheDistricts.features[i];
100845
+
100846
+ if (f.geometry.type === 'Polygon')
100847
+ {
100848
+ // Push all holes from this polygon
100849
+ for (j = 1;j < f.geometry.coordinates.length;j++)
100850
+ {
100851
+ let p: any = f.geometry.coordinates[j];
100852
+ if (oUnique[Hash.qhash(p)] === undefined)
100853
+ af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: [p]}});
100854
+ }
100855
+ }
100856
+ }
100857
+ } */
98715
100858
  // HELPERS USED INTERNALLY
98716
100859
  // Get an individual test, so you can drive UI with the results.
98717
100860
  getTest(testID) {
@@ -98727,9 +100870,21 @@ class AnalyticsSession {
98727
100870
  // Return a pointer to the the test entry for this test
98728
100871
  return this.tests[testID];
98729
100872
  }
98730
- // NOTE - Not sure why this has to be up here.
100873
+ // NOTE - Not sure why this has to be up here ...
98731
100874
  populationDeviationThreshold() {
98732
- return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
100875
+ // TODO - SCORE: Toggle
100876
+ if (this.useLegacy()) {
100877
+ return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
100878
+ }
100879
+ else {
100880
+ // NOTE - This assumes the plan has been profiled
100881
+ const scorer = new Score.Scorer();
100882
+ const popdev = scorer.populationDeviationThreshold(this.legislativeDistricts);
100883
+ return popdev;
100884
+ // TODO - SCORE: Temporary HACK. Query dra-score for threshold.
100885
+ // NOTE - The plan may not have been scored yet, i.e., no scorecard yet.
100886
+ // return 1 - this.testScales[T.Test.PopulationDeviation]['scale'][0];
100887
+ }
98733
100888
  }
98734
100889
  }
98735
100890
  exports.AnalyticsSession = AnalyticsSession;
@@ -98905,7 +101060,6 @@ class Districts {
98905
101060
  let outerThis = this;
98906
101061
  // Default the pop dev % for the dummy Unassigned district to 0%.
98907
101062
  // Default the pop dev % for real (1–N) but empty districts to 100%.
98908
- // TODO - SCORE
98909
101063
  let popDevPct = (i > 0) ? (targetSize / targetSize) : 0 / targetSize;
98910
101064
  // Get the geoIDs assigned to the district
98911
101065
  // Guard against empty districts
@@ -98932,7 +101086,6 @@ class Districts {
98932
101086
  // NOTE - SPLITTING
98933
101087
  // Total population by counties w/in a district,
98934
101088
  // except the dummy unassigned district 0
98935
- // TODO - VFEATURE
98936
101089
  if (i > 0)
98937
101090
  countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
98938
101091
  // Democratic and Republican vote totals
@@ -99106,10 +101259,7 @@ class Districts {
99106
101259
  compact_1.extractDistrictProperties(this._session, bLog);
99107
101260
  }
99108
101261
  getCountyIndex(geoID) {
99109
- // TODO - VFEATURE
99110
101262
  let countyFIPS = U.parseGeoID(geoID)['county'];
99111
- // let countyGeoID = U.parseGeoID(geoID)['county'] as string;
99112
- // let countyFIPS = U.getFIPSFromCountyGeoID(countyGeoID);
99113
101263
  let countyIndex = this._session.counties.indexFromFIPS(countyFIPS);
99114
101264
  return countyIndex;
99115
101265
  }
@@ -99358,11 +101508,45 @@ exports.doAnalyzeDistricts = doAnalyzeDistricts;
99358
101508
  // NOTE - I could make this table-driven, but I'm thinking that the explicit
99359
101509
  // calls might make chunking for aync easier.
99360
101510
  function doAnalyzePlan(s, bLog = false) {
99361
- // TODO - Remove this mechanism. Always run all tests
99362
- // Get the requested suites, and only execute those tests
99363
- let requestedSuites = s.config['suites'];
99364
- // Tests in the "Legal" suite, i.e., pass/ fail constraints
99365
- if (requestedSuites.includes(0 /* Legal */)) {
101511
+ // TODO - SCORE: Toggle
101512
+ if (s.useLegacy()) {
101513
+ // Disable most legacy analytics
101514
+ // Get the requested suites, and only execute those tests
101515
+ let requestedSuites = s.config['suites'];
101516
+ // Tests in the "Legal" suite, i.e., pass/ fail constraints
101517
+ if (requestedSuites.includes(0 /* Legal */)) {
101518
+ s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
101519
+ s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
101520
+ s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
101521
+ s.tests[4 /* PopulationDeviation */] = equal_1.doPopulationDeviation(s, bLog);
101522
+ // NOTE - I can't check whether a population deviation is legal or not, until
101523
+ // the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
101524
+ // the given type of district (CD vs. LD). The EqualPopulation test is derived
101525
+ // from PopulationDeviation, as part of scorecard or test log preparation.
101526
+ // Create an empty test entry here though ...
101527
+ s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
101528
+ }
101529
+ // Tests in the "Fair" suite
101530
+ if (requestedSuites.includes(1 /* Fair */)) {
101531
+ s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
101532
+ s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
101533
+ s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
101534
+ s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
101535
+ s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
101536
+ s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
101537
+ }
101538
+ // Tests in the "Best" suite, i.e., criteria for better/worse
101539
+ if (requestedSuites.includes(2 /* Best */)) {
101540
+ s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
101541
+ s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
101542
+ s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
101543
+ s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
101544
+ s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
101545
+ s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
101546
+ }
101547
+ }
101548
+ else {
101549
+ // TODO - SCORE: Except these. Continue to do these here vs. dra-score.
99366
101550
  s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
99367
101551
  s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
99368
101552
  s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
@@ -99373,24 +101557,8 @@ function doAnalyzePlan(s, bLog = false) {
99373
101557
  // from PopulationDeviation, as part of scorecard or test log preparation.
99374
101558
  // Create an empty test entry here though ...
99375
101559
  s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
99376
- }
99377
- // Tests in the "Fair" suite
99378
- if (requestedSuites.includes(1 /* Fair */)) {
99379
- s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
99380
- s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
99381
- s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
99382
- s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
99383
- s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
99384
- s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
99385
- }
99386
- // Tests in the "Best" suite, i.e., criteria for better/worse
99387
- if (requestedSuites.includes(2 /* Best */)) {
99388
- s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
99389
- s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
99390
101560
  s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
99391
101561
  s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
99392
- s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
99393
- s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
99394
101562
  }
99395
101563
  // Enable a Test Log and Scorecard to be generated
99396
101564
  s.bPlanAnalyzed = true;
@@ -99987,6 +102155,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
99987
102155
  Object.defineProperty(exports, "__esModule", { value: true });
99988
102156
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
99989
102157
  const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
102158
+ // TODO - SCORE: Delete
99990
102159
  function doPopulationDeviation(s, bLog = false) {
99991
102160
  let test = s.getTest(4 /* PopulationDeviation */);
99992
102161
  let targetSize = s.state.totalPop / s.state.nDistricts;
@@ -100025,8 +102194,8 @@ function doHasEqualPopulations(s, bLog = false) {
100025
102194
  let test = s.getTest(3 /* EqualPopulation */);
100026
102195
  // Get the normalized population deviation %
100027
102196
  let popDevTest = s.getTest(4 /* PopulationDeviation */);
100028
- let popDevPct = popDevTest['score'];
100029
- let popDevNormalized = popDevTest['normalizedScore'];
102197
+ const popDevPct = popDevTest['score'];
102198
+ const popDevNormalized = popDevTest['normalizedScore'];
100030
102199
  // Populate the test entry
100031
102200
  if (popDevNormalized > 0) {
100032
102201
  test['score'] = true;
@@ -100068,6 +102237,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
100068
102237
  };
100069
102238
  Object.defineProperty(exports, "__esModule", { value: true });
100070
102239
  const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "./node_modules/@dra2020/poly/dist/poly.js"));
102240
+ // HELPER
102241
+ function polyParts(poly) {
102242
+ let parts = { type: 'FeatureCollection', features: [] };
102243
+ let af = parts.features;
102244
+ if (poly.geometry.type === 'MultiPolygon') {
102245
+ // Push all non-contiguous polygons
102246
+ for (let j = 0; j < poly.geometry.coordinates.length; j++) {
102247
+ let onePoly = poly.geometry.coordinates[j];
102248
+ af.push({ type: 'Feature', properties: { id: `${af.length + 1}` }, geometry: { type: 'Polygon', coordinates: onePoly } });
102249
+ }
102250
+ }
102251
+ else {
102252
+ parts.features.push(poly);
102253
+ }
102254
+ return parts;
102255
+ }
102256
+ exports.polyParts = polyParts;
100071
102257
  // CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
100072
102258
  // TODO - POLY: Confirm Cartesian calculations
100073
102259
  function gfArea(poly) {
@@ -100369,10 +102555,7 @@ function doPreprocessCensus(s, bLog = false) {
100369
102555
  // Sum total population across the state
100370
102556
  s.state.totalPop += value;
100371
102557
  // Get the county FIPS code for the feature
100372
- // TODO - VFEATURE
100373
102558
  let countyFIPS = U.parseGeoID(geoID)['county'];
100374
- // let county = U.parseGeoID(geoID)['county'] as string;
100375
- // let countyFIPS = U.getFIPSFromCountyGeoID(county);
100376
102559
  // If a subtotal for the county doesn't exist, initialize one
100377
102560
  if (!(U.keyExists(countyFIPS, totalByCounty))) {
100378
102561
  totalByCounty[countyFIPS] = 0;
@@ -100390,8 +102573,8 @@ function doPreprocessCensus(s, bLog = false) {
100390
102573
  let fipsCodes = U.getObjectKeys(totalByCounty);
100391
102574
  // Sort the results
100392
102575
  fipsCodes = fipsCodes.sort();
100393
- // TODO - SCORE: This was added for SPLITTING
100394
- // Add a dummy county, for county-district splitting analysis
102576
+ // NOTE - This was added for the legacy SPLITTING implementation.
102577
+ // Add a dummy county, for county-district splitting analysis.
100395
102578
  fipsCodes.unshift('000');
100396
102579
  // Create the ID-ordinal map
100397
102580
  for (let i in fipsCodes) {
@@ -100481,6 +102664,7 @@ const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
100481
102664
  const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
100482
102665
  const state_reqs_json_1 = __importDefault(__webpack_require__(/*! ../static/state-reqs.json */ "./static/state-reqs.json"));
100483
102666
  // Example
102667
+ // TODO - DELETE?
100484
102668
  let sampleRequirements = {
100485
102669
  score: 2 /* Red */,
100486
102670
  metrics: {
@@ -100504,6 +102688,7 @@ let sampleRequirements = {
100504
102688
  stateReqs: "https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_20.pdf"
100505
102689
  }
100506
102690
  };
102691
+ // TODO - DELETE
100507
102692
  let sampleCompactness = {
100508
102693
  score: 60,
100509
102694
  metrics: {
@@ -100516,6 +102701,7 @@ let sampleCompactness = {
100516
102701
  },
100517
102702
  resources: {}
100518
102703
  };
102704
+ // TODO - DELETE
100519
102705
  let sampleSplitting = {
100520
102706
  score: 73,
100521
102707
  metrics: {
@@ -100533,7 +102719,7 @@ let sampleSplitting = {
100533
102719
  datasets: {},
100534
102720
  resources: {}
100535
102721
  };
100536
- // TODO - PARTISAN: This category is still being fleshed out.
102722
+ // TODO - DELETE
100537
102723
  let samplePartisan = {
100538
102724
  score: 100,
100539
102725
  metrics: {
@@ -100548,7 +102734,7 @@ let samplePartisan = {
100548
102734
  planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
100549
102735
  }
100550
102736
  };
100551
- // TODO - SCORE: Add a minority report
102737
+ // TODO - DELETE
100552
102738
  let sampleMinority = {
100553
102739
  score: null,
100554
102740
  metrics: {
@@ -100575,6 +102761,7 @@ let sampleMinority = {
100575
102761
  },
100576
102762
  resources: {}
100577
102763
  };
102764
+ // TODO - DELETE
100578
102765
  exports.samplePlanAnalytics = {
100579
102766
  requirements: sampleRequirements,
100580
102767
  compactness: sampleCompactness,
@@ -100583,232 +102770,6 @@ exports.samplePlanAnalytics = {
100583
102770
  partisan: samplePartisan,
100584
102771
  minority: sampleMinority
100585
102772
  };
100586
- /* TODO - SCORE
100587
- export function preparePlanAnalytics(s: AnalyticsSession, bLog: boolean = false): PlanAnalytics
100588
- {
100589
- if (!(s.bPostProcessingDone))
100590
- {
100591
- doAnalyzePostProcessing(s);
100592
- }
100593
-
100594
- // REQUIREMENTS CATEGORY
100595
- let paRequirements: RequirementsCategory;
100596
-
100597
- {
100598
- let completeTest = s.getTest(T.Test.Complete) as T.TestEntry;
100599
- let contiguousTest = s.getTest(T.Test.Contiguous) as T.TestEntry;
100600
- let freeOfHolesTest = s.getTest(T.Test.FreeOfHoles) as T.TestEntry;
100601
- let equalPopulationTest = s.getTest(T.Test.EqualPopulation) as T.TestEntry;
100602
-
100603
- // Combine individual checks into an overall score
100604
-
100605
- // TODO - DASHBOARD: Until we add three-state support top to bottom in
100606
- // requirements/validations, map booleans to tri-states here.
100607
- let completeMetric = U.mapBooleanToTriState(completeTest['score'] as boolean);
100608
- let contiguousMetric = U.mapBooleanToTriState(contiguousTest['score'] as boolean);
100609
- let freeOfHolesMetric = U.mapBooleanToTriState(freeOfHolesTest['score'] as boolean);
100610
- let equalPopulationMetric = U.mapBooleanToTriState(equalPopulationTest['score'] as boolean);
100611
-
100612
- let reqScore: T.TriState = T.TriState.Green;
100613
- let checks = [completeMetric, contiguousMetric, freeOfHolesMetric, equalPopulationMetric];
100614
- if (checks.includes(T.TriState.Yellow)) reqScore = T.TriState.Yellow;
100615
- if (checks.includes(T.TriState.Red)) reqScore = T.TriState.Red;
100616
-
100617
- // Get values to support details entries
100618
- let unassignedFeaturesDetail = U.deepCopy(completeTest['details']['unassignedFeatures']) || [];
100619
- let emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
100620
- let discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
100621
- let embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
100622
-
100623
- let populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
100624
- let deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
100625
-
100626
- let xx: string = s.state.xx;
100627
- // TODO - JSON: Is there a better / easier way to work with the variable?
100628
- let stateReqsDict: T.Dict = allStateReqs;
100629
- let reqLinkToStateReqs: string = stateReqsDict[xx];
100630
-
100631
- // Populate the category
100632
- paRequirements = {
100633
- score: reqScore,
100634
- metrics: {
100635
- complete: completeMetric,
100636
- contiguous: contiguousMetric,
100637
- freeOfHoles: freeOfHolesMetric,
100638
- equalPopulation: equalPopulationMetric
100639
- },
100640
- details: {
100641
- unassignedFeatures: unassignedFeaturesDetail,
100642
- emptyDistricts: emptyDistrictsDetail,
100643
- discontiguousDistricts: discontiguousDistrictsDetail,
100644
- embeddedDistricts: embeddedDistrictsDetail,
100645
- populationDeviation: populationDeviationDetail,
100646
- deviationThreshold: deviationThresholdDetail
100647
- },
100648
- datasets: {
100649
- census: U.deepCopy(s.config['descriptions']['CENSUS']),
100650
- },
100651
- resources: {
100652
- stateReqs: reqLinkToStateReqs
100653
- }
100654
- }
100655
- }
100656
-
100657
- // COMPACTNESS CATEGORY
100658
- let paCompactness: CompactnessCategory;
100659
- {
100660
- let reockWeight = 0.5;
100661
- let polsbyWeight = 1.0 - reockWeight;
100662
-
100663
- let reockTest = s.getTest(T.Test.Reock) as T.TestEntry;
100664
- let polsbyTest = s.getTest(T.Test.PolsbyPopper) as T.TestEntry;
100665
-
100666
- let normalizedReock = reockTest['normalizedScore'] as number;
100667
- let normalizedPolsby = reockTest['normalizedScore'] as number;
100668
- let compactnessScore = U.trim((reockWeight * normalizedReock) + (polsbyWeight * normalizedPolsby), 0);
100669
-
100670
- let reockMetric = U.deepCopy(reockTest['score'] as number);
100671
- let polsbyMetric = U.deepCopy(polsbyTest['score'] as number);
100672
-
100673
- // Populate the category
100674
- paCompactness = {
100675
- score: compactnessScore,
100676
- metrics: {
100677
- reock: reockMetric,
100678
- polsby: polsbyMetric
100679
- },
100680
- details: {
100681
- // None at this time
100682
- },
100683
- datasets: {
100684
- // NOTE - DATASETS
100685
- shapes: U.deepCopy(s.config['descriptions']['SHAPES'])
100686
- // shapes: "2010 VTD shapes"
100687
- },
100688
- resources: {
100689
- // None at this time
100690
- }
100691
- }
100692
- }
100693
-
100694
- // SPLITTING CATEGORY
100695
-
100696
- let paSplitting: SplittingCategory
100697
-
100698
- {
100699
- let unexpectedCountySplittingTest = s.getTest(T.Test.UnexpectedCountySplits) as T.TestEntry;
100700
- let VTDSplitsTest = s.getTest(T.Test.VTDSplits) as T.TestEntry;
100701
- let countySplittingTest = s.getTest(T.Test.CountySplitting) as T.TestEntry;
100702
- let districtSplittingTest = s.getTest(T.Test.DistrictSplitting) as T.TestEntry;
100703
-
100704
- let unexpectedAffectedMetric = U.deepCopy(unexpectedCountySplittingTest['score']);
100705
- let countiesSplitUnexpectedlyDetail = U.deepCopy(unexpectedCountySplittingTest['details']['countiesSplitUnexpectedly']);
100706
-
100707
- let nVTDSplitsMetric = U.deepCopy(VTDSplitsTest['score']);
100708
- let splitVTDsDetail = U.deepCopy(VTDSplitsTest['details']['splitVTDs']);
100709
-
100710
- let SqEnt_DCreducedMetric = U.deepCopy(countySplittingTest['score']);
100711
- let SqEnt_CDreducedMetric = U.deepCopy(districtSplittingTest['score']);
100712
-
100713
- let countySplittingNormalized = countySplittingTest['normalizedScore'] as number;
100714
- let districtSplittingNormalized = districtSplittingTest['normalizedScore'] as number;
100715
- let splittingScore = U.trim((S.COUNTY_SPLITTING_WEIGHT * countySplittingNormalized) +
100716
- + (S.DISTRICT_SPLITTING_WEIGHT * districtSplittingNormalized), 0);
100717
-
100718
- paSplitting = {
100719
- score: splittingScore,
100720
- metrics: {
100721
- sqEnt_DCreduced: SqEnt_DCreducedMetric,
100722
- sqEnt_CDreduced: SqEnt_CDreducedMetric
100723
- // NOTE - The un-reduced raw values
100724
- // sqEnt_DC : SqEnt_DCMetric,
100725
- // sqEnt_CD : SqEnt_CDMetric
100726
- },
100727
- details: {
100728
- countiesSplitUnexpectedly: countiesSplitUnexpectedlyDetail,
100729
- unexpectedAffected: unexpectedAffectedMetric,
100730
- nSplitVTDs: nVTDSplitsMetric,
100731
- splitVTDs: splitVTDsDetail
100732
- },
100733
- datasets: {
100734
- // None at this time
100735
- },
100736
- resources: {
100737
- // None at this time
100738
- }
100739
- }
100740
- }
100741
-
100742
- // PARTISAN CATEGORY
100743
- //
100744
- // TODO - PARTISAN: This category is still being fleshed out. Just an example below.
100745
- let paPartisan: PartisanCategory;
100746
-
100747
- {
100748
- paPartisan = {
100749
- score: 100,
100750
- metrics: {
100751
- partisanBias: 0.15,
100752
- responsiveness: 2.0
100753
- },
100754
- details: {},
100755
- datasets: {
100756
- election: "2016 Presidential, US Senate, Governor, and AG election results"
100757
- },
100758
- resources: {
100759
- planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
100760
- }
100761
- }
100762
- }
100763
-
100764
- // MINORITY CATEGORY
100765
- //
100766
- // TODO - MINORITY: This category is still being fleshed out. Just an example below.
100767
- let paMinority: MinorityCategory;
100768
-
100769
- {
100770
- paMinority = {
100771
- score: null,
100772
- metrics: {
100773
- nBlack37to50: 1,
100774
- nBlackMajority: 12,
100775
- nHispanic37to50: 0,
100776
- nHispanicMajority: 0,
100777
- nPacific37to50: 0,
100778
- nPacificMajority: 0,
100779
- nAsian37to50: 0,
100780
- nAsianMajority: 0,
100781
- nNative37to50: 0,
100782
- nNativeMajority: 0,
100783
- nMinority37to50: 0,
100784
- nMinorityMajority: 0,
100785
-
100786
- averageDVoteShare: 0.90
100787
- },
100788
- details: {
100789
- vap: true,
100790
- comboCategories: true
100791
- },
100792
- datasets: {
100793
- vap: "2010 Voting Age Population"
100794
- },
100795
- resources: {}
100796
- }
100797
- }
100798
-
100799
- // PLAN ANALYTICS
100800
- let pa: PlanAnalytics = {
100801
- requirements: paRequirements,
100802
- compactness: paCompactness,
100803
- // TODO - Not implemented yet
100804
- splitting: paSplitting,
100805
- partisan: paPartisan,
100806
- minority: paMinority
100807
- }
100808
-
100809
- return pa;
100810
- }
100811
- */
100812
102773
  function prepareRequirementsChecklist(s, bLog = false) {
100813
102774
  if (!(s.bPostProcessingDone)) {
100814
102775
  doAnalyzePostProcessing(s);
@@ -100816,34 +102777,40 @@ function prepareRequirementsChecklist(s, bLog = false) {
100816
102777
  // REQUIREMENTS CATEGORY
100817
102778
  let paRequirements;
100818
102779
  {
100819
- let completeTest = s.getTest(0 /* Complete */);
100820
- let contiguousTest = s.getTest(1 /* Contiguous */);
100821
- let freeOfHolesTest = s.getTest(2 /* FreeOfHoles */);
100822
- let equalPopulationTest = s.getTest(3 /* EqualPopulation */);
102780
+ const completeTest = s.getTest(0 /* Complete */);
102781
+ const contiguousTest = s.getTest(1 /* Contiguous */);
102782
+ const freeOfHolesTest = s.getTest(2 /* FreeOfHoles */);
102783
+ const equalPopulationTest = s.getTest(3 /* EqualPopulation */);
100823
102784
  // Combine individual checks into an overall score
100824
- // TODO - DASHBOARD: Until we add three-state support top to bottom in
102785
+ // NOTE - Until we add three-state support top to bottom in
100825
102786
  // requirements/validations, map booleans to tri-states here.
100826
- let completeMetric = U.mapBooleanToTriState(completeTest['score']);
100827
- let contiguousMetric = U.mapBooleanToTriState(contiguousTest['score']);
100828
- let freeOfHolesMetric = U.mapBooleanToTriState(freeOfHolesTest['score']);
100829
- let equalPopulationMetric = U.mapBooleanToTriState(equalPopulationTest['score']);
102787
+ const completeMetric = U.mapBooleanToTriState(completeTest['score']);
102788
+ const contiguousMetric = U.mapBooleanToTriState(contiguousTest['score']);
102789
+ const freeOfHolesMetric = U.mapBooleanToTriState(freeOfHolesTest['score']);
102790
+ const equalPopulationMetric = U.mapBooleanToTriState(equalPopulationTest['score']);
100830
102791
  let reqScore = 0 /* Green */;
100831
- let checks = [completeMetric, contiguousMetric, freeOfHolesMetric, equalPopulationMetric];
102792
+ const checks = [completeMetric, contiguousMetric, freeOfHolesMetric, equalPopulationMetric];
100832
102793
  if (checks.includes(1 /* Yellow */))
100833
102794
  reqScore = 1 /* Yellow */;
100834
102795
  if (checks.includes(2 /* Red */))
100835
102796
  reqScore = 2 /* Red */;
100836
102797
  // Get values to support details entries
100837
- let unassignedFeaturesDetail = U.deepCopy(completeTest['details']['unassignedFeatures']) || [];
100838
- let emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
100839
- let discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
100840
- let embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
100841
- let populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
100842
- let deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
100843
- let xx = s.state.xx;
102798
+ const unassignedFeaturesDetail = U.deepCopy(completeTest['details']['unassignedFeatures']) || [];
102799
+ const emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
102800
+ const discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
102801
+ const embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
102802
+ // TODO - SCORE: DELETE - This code is hooked correctly to use dra-score
102803
+ // const scorecard = s._scorecard as Score.Scorecard;
102804
+ // const populationDeviation = scorecard.best.populationDeviation.raw;
102805
+ const populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
102806
+ // console.log("Population deviations =", populationDeviationDetail, populationDeviation);
102807
+ // const deviationThreshold = scorecard.best.populationDeviation.notes['threshold'];
102808
+ const deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
102809
+ // console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
102810
+ const xx = s.state.xx;
100844
102811
  // TODO - JSON: Is there a better / easier way to work with the variable?
100845
- let stateReqsDict = state_reqs_json_1.default;
100846
- let reqLinkToStateReqs = stateReqsDict[xx];
102812
+ const stateReqsDict = state_reqs_json_1.default;
102813
+ const reqLinkToStateReqs = stateReqsDict[xx];
100847
102814
  // Populate the category
100848
102815
  paRequirements = {
100849
102816
  score: reqScore,
@@ -101106,19 +103073,30 @@ exports.doConfigureScales = doConfigureScales;
101106
103073
  // Postprocess analytics - Normalize numeric results and derive secondary tests.
101107
103074
  // Do this after analytics have been run and before preparing a test log or scorecard.
101108
103075
  function doAnalyzePostProcessing(s, bLog = false) {
101109
- // Normalize the raw scores for all the numerics tests
101110
- let testResults = U.getNumericObjectKeys(testDefns);
101111
- for (let testID of testResults) {
101112
- if (testDefns[testID]['normalize']) {
101113
- let testResult = s.getTest(testID);
101114
- let rawScore = testResult['score'];
101115
- let normalizedScore;
101116
- normalizedScore = U.normalize(rawScore, s.testScales[testID]);
101117
- testResult['normalizedScore'] = normalizedScore;
101118
- // Add the scale used to normalize the raw score to the details
101119
- testResult['details']['scale'] = s.testScales[testID].scale;
103076
+ // TODO - SCORE: Toggle
103077
+ if (s.useLegacy()) {
103078
+ // Normalize the raw scores for all the numerics tests
103079
+ let testResults = U.getNumericObjectKeys(testDefns);
103080
+ for (let testID of testResults) {
103081
+ if (testDefns[testID]['normalize']) {
103082
+ let testResult = s.getTest(testID);
103083
+ let rawScore = testResult['score'];
103084
+ let normalizedScore;
103085
+ normalizedScore = U.normalize(rawScore, s.testScales[testID]);
103086
+ testResult['normalizedScore'] = normalizedScore;
103087
+ // Add the scale used to normalize the raw score to the details
103088
+ testResult['details']['scale'] = s.testScales[testID].scale;
103089
+ }
101120
103090
  }
101121
103091
  }
103092
+ else {
103093
+ // Just populate the normalized population deviation score in the test
103094
+ const scorecard = s._scorecard;
103095
+ let test = s.getTest(4 /* PopulationDeviation */);
103096
+ test['normalizedScore'] = scorecard.traditionalPrinciples.populationDeviation.normalized;
103097
+ // TODO - DELETE
103098
+ // test['normalizedScore'] = scorecard.best.populationDeviation.normalized;
103099
+ }
101122
103100
  // Derive secondary tests
101123
103101
  analyze_1.doDeriveSecondaryTests(s, bLog);
101124
103102
  // Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
@@ -101163,7 +103141,7 @@ function profilePlan(s, bLog = false) {
101163
103141
  const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
101164
103142
  const splits = makeNakedCxD(s);
101165
103143
  const summaryRow = s.districts.numberOfRows() - 1;
101166
- const statewideVf = s.districts.statistics[D.DistrictField.DemPct][summaryRow];
103144
+ const statewideVf = U.trim(s.districts.statistics[D.DistrictField.DemPct][summaryRow], 6);
101167
103145
  const vpiArray = U.deepCopy(s.districts.statistics[D.DistrictField.DemPct].slice(1, -1));
101168
103146
  const demographicsByDistrict = makeArrayOfDemographics(s);
101169
103147
  const profile = {
@@ -101201,13 +103179,11 @@ function makeNakedCxD(s, bLog = false) {
101201
103179
  let CxD = [];
101202
103180
  // Remove the unassigned & total dummy "districts"
101203
103181
  for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
101204
- // TODO - SCORE: OH has an extra county!?!
101205
103182
  const splits = U.deepCopy(adornedCxD[districtID].slice(1));
101206
103183
  CxD.push(splits);
101207
103184
  }
101208
103185
  return CxD;
101209
103186
  }
101210
- // TODO - SCORE: Convert dict of geo props to array by district index
101211
103187
  function makeArrayOfGeoProps(s, bLog = false) {
101212
103188
  let geometryByDistrict = [];
101213
103189
  for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
@@ -101241,9 +103217,30 @@ function makeArrayOfDemographics(s, bLog = false) {
101241
103217
  return demographicsArray;
101242
103218
  }
101243
103219
  // SCORE A PLAN
101244
- function scorePlan(p, bLog = false) {
103220
+ function scorePlan(s, p, bLog = false, overridesJSON) {
101245
103221
  let scorer = new Score.Scorer();
101246
- return scorer.score(p);
103222
+ const scorecard = scorer.score(p, overridesJSON);
103223
+ // TODO - SCORE: Toggle: Before returning, create a dummy population deviation
103224
+ // test, for doHasEqualPopulations() to use later. This is preserving the old
103225
+ // calling sequence.
103226
+ let test = s.getTest(4 /* PopulationDeviation */);
103227
+ // TODO - SCORE: U.trim(popDev)???
103228
+ // const popDev = scorecard.best.populationDeviation.raw;
103229
+ // Get the raw population deviation
103230
+ const popDev = scorecard.traditionalPrinciples.populationDeviation.raw;
103231
+ // Populate the test entry
103232
+ test['score'] = popDev;
103233
+ test['details'] = { 'maxDeviation': scorecard.traditionalPrinciples.populationDeviation.notes['maxDeviation'] };
103234
+ // TODO - DELETE
103235
+ // test['details'] = { 'maxDeviation': scorecard.best.populationDeviation.notes['maxDeviation'] };
103236
+ // Populate the N+1 summary "district" in district.statistics
103237
+ let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
103238
+ let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
103239
+ let summaryRow = s.districts.numberOfRows() - 1;
103240
+ totalPop[summaryRow] = p.populationProfile.targetSize;
103241
+ popDevPct[summaryRow] = popDev;
103242
+ //
103243
+ return scorecard;
101247
103244
  }
101248
103245
  exports.scorePlan = scorePlan;
101249
103246
 
@@ -101305,6 +103302,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
101305
103302
  return result;
101306
103303
  };
101307
103304
  Object.defineProperty(exports, "__esModule", { value: true });
103305
+ const DT = __importStar(__webpack_require__(/*! @dra2020/dra-types */ "./node_modules/@dra2020/dra-types/dist/dra-types.js"));
101308
103306
  const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
101309
103307
  // PLAN HELPERS
101310
103308
  // Is a "neighbor" in state?
@@ -101332,7 +103330,6 @@ function getDistrict(plan, geoID) {
101332
103330
  }
101333
103331
  exports.getDistrict = getDistrict;
101334
103332
  // WORKING WITH GEOIDS
101335
- // TODO - VFEATURE
101336
103333
  function parseGeoID(geoID) {
101337
103334
  let bVfeature = false;
101338
103335
  // Rewrite vfeature GEOIDs to enable lexical parsing of higher-level parts
@@ -101340,12 +103337,7 @@ function parseGeoID(geoID) {
101340
103337
  // Example: vfeature_39153153ASV_0_28e0bc2c8163e5982e1da2d61e2388a8325c575e
101341
103338
  if (geoID.indexOf('vfeature') >= 0) {
101342
103339
  bVfeature = true;
101343
- // Strip off leading 'vfeature_'
101344
- let parentGeoID = geoID.slice(9);
101345
- // Strip off trailing goo
101346
- const uPos = parentGeoID.indexOf('_');
101347
- parentGeoID = parentGeoID.slice(0, uPos);
101348
- geoID = parentGeoID;
103340
+ geoID = DT.vgeoidToGeoid(geoID);
101349
103341
  }
101350
103342
  const parts = {
101351
103343
  vfeature: bVfeature,
@@ -101353,29 +103345,9 @@ function parseGeoID(geoID) {
101353
103345
  county: geoID.substring(2, 5),
101354
103346
  rest: geoID.slice(5)
101355
103347
  };
101356
- // let l: number = geoID.length;
101357
- // if (l >= 11)
101358
- // {
101359
- // parts['tract'] = geoID.substring(0, 11);
101360
- // }
101361
- // if (l >= 12)
101362
- // {
101363
- // parts['bg'] = geoID.substring(0, 12);
101364
- // }
101365
- // if (l == 15)
101366
- // {
101367
- // parts['block'] = geoID;
101368
- // }
101369
103348
  return parts;
101370
103349
  }
101371
103350
  exports.parseGeoID = parseGeoID;
101372
- // TODO - VFEATURE
101373
- // export function getFIPSFromCountyGeoID(geoID: string): string
101374
- // {
101375
- // const fips = geoID.substring(2, 5);
101376
- // if (isNaN(Number(fips))) console.log("Non-numeric GEOID =", geoID);
101377
- // return fips;
101378
- // }
101379
103351
  function isWaterOnly(geoID) {
101380
103352
  let waterOnlySignature = 'ZZZZZZ';
101381
103353
  if (geoID.indexOf(waterOnlySignature) >= 0)
@@ -102276,113 +104248,6 @@ function formatNumber(n) {
102276
104248
  let p = S.PRECISION;
102277
104249
  return n.toLocaleString('en-US', { minimumFractionDigits: p, maximumFractionDigits: p });
102278
104250
  }
102279
- /* TODO - SCORE: DELETE
102280
- function echoPlanAnalytics(pa: PlanAnalytics): void
102281
- {
102282
- console.log("Plan Analytics:");
102283
- console.log("");
102284
-
102285
- console.log("Requirements:");
102286
- console.log("* Score:", pa.requirements.score);
102287
- console.log("* Metrics:");
102288
- console.log(" - Complete:", pa.requirements.metrics.complete);
102289
- console.log(" - Contiguous:", pa.requirements.metrics.contiguous);
102290
- console.log(" - Free of holes:", pa.requirements.metrics.freeOfHoles);
102291
- console.log(" - Equal population:", pa.requirements.metrics.equalPopulation);
102292
- console.log("* Details:");
102293
- console.log(" - Empty districts:", prepareListItems(pa.requirements.details.emptyDistricts));
102294
- console.log(" - Unassigned features:", prepareListItems(pa.requirements.details.unassignedFeatures));
102295
- console.log(" - Population deviation:", pa.requirements.details.populationDeviation);
102296
- console.log(" - Deviation threshold:", pa.requirements.details.deviationThreshold);
102297
- console.log(" - Discontiguous districts:", prepareListItems(pa.requirements.details.discontiguousDistricts));
102298
- console.log(" - Embedded districts:", prepareListItems(pa.requirements.details.embeddedDistricts));
102299
- console.log("* Datasets:");
102300
- console.log("- Shapes:", pa.requirements.datasets.shapes);
102301
- console.log("- Census:", pa.requirements.datasets.census);
102302
- console.log("- VAP:", pa.requirements.datasets.vap);
102303
- console.log("- Election:", pa.requirements.datasets.election);
102304
- console.log("* Resources:");
102305
- console.log("- State requirements:", pa.requirements.resources.stateReqs);
102306
- console.log("");
102307
-
102308
- console.log("Compactness:");
102309
- console.log("* Score:", pa.compactness.score);
102310
- console.log("* Metrics:");
102311
- console.log(" - Reock:", pa.compactness.metrics.reock);
102312
- console.log(" - Polsby-Popper:", pa.compactness.metrics.polsby);
102313
- console.log("* Details:");
102314
- console.log(" - N/A");
102315
- console.log("* Datasets:");
102316
- console.log("- Shapes:", pa.compactness.datasets.shapes);
102317
- console.log("- Census:", pa.compactness.datasets.census);
102318
- console.log("- VAP:", pa.compactness.datasets.vap);
102319
- console.log("- Election:", pa.compactness.datasets.election);
102320
- console.log("* Resources:");
102321
- console.log("- N/A");
102322
- console.log("");
102323
-
102324
- console.log("Splitting:");
102325
- console.log("* Score:", pa.splitting.score);
102326
- console.log("* Metrics:");
102327
- console.log(" - sqEnt_DCreduced:", pa.splitting.metrics.sqEnt_DCreduced);
102328
- console.log(" - sqEnt_CDreduced:", pa.splitting.metrics.sqEnt_CDreduced);
102329
- console.log("* Details:");
102330
- console.log(" - countiesSplitUnexpectedly:", prepareListItems(pa.splitting.details.countiesSplitUnexpectedly));
102331
- console.log(" - unexpectedAffected:", pa.splitting.details.unexpectedAffected);
102332
- console.log(" - splitVTDs:", pa.splitting.details.splitVTDs);
102333
- console.log("* Datasets:");
102334
- console.log("- Shapes:", pa.splitting.datasets.shapes);
102335
- console.log("- Census:", pa.splitting.datasets.census);
102336
- console.log("- VAP:", pa.splitting.datasets.vap);
102337
- console.log("- Election:", pa.splitting.datasets.election);
102338
- console.log("* Resources:");
102339
- console.log("- N/A");
102340
- console.log("");
102341
-
102342
- console.log("Partisan:");
102343
- console.log("* Score:", pa.partisan.score);
102344
- console.log("* Metrics:");
102345
- console.log(" - sqEnt_DCreduced:", pa.partisan.metrics.partisanBias);
102346
- console.log(" - sqEnt_CDreduced:", pa.partisan.metrics.responsiveness);
102347
- console.log("* Details:");
102348
- console.log(" - N/A");
102349
- console.log("* Datasets:");
102350
- console.log("- Shapes:", pa.partisan.datasets.shapes);
102351
- console.log("- Census:", pa.partisan.datasets.census);
102352
- console.log("- VAP:", pa.partisan.datasets.vap);
102353
- console.log("- Election:", pa.partisan.datasets.election);
102354
- console.log("* Resources:");
102355
- console.log(" - Plan Score:", pa.partisan.resources.planScore);
102356
- console.log("");
102357
-
102358
- console.log("Minority:");
102359
- console.log("* Score:", pa.minority.score);
102360
- console.log("* Metrics:");
102361
- console.log(" - nBlack37to50:", pa.minority.metrics.nBlack37to50);
102362
- console.log(" - nBlackMajority:", pa.minority.metrics.nBlackMajority);
102363
- console.log(" - nHispanic37to50:", pa.minority.metrics.nHispanic37to50);
102364
- console.log(" - nHispanicMajority:", pa.minority.metrics.nHispanicMajority);
102365
- console.log(" - nPacific37to50:", pa.minority.metrics.nPacific37to50);
102366
- console.log(" - nPacificMajority:", pa.minority.metrics.nPacificMajority);
102367
- console.log(" - nAsian37to50:", pa.minority.metrics.nAsian37to50);
102368
- console.log(" - nAsianMajority:", pa.minority.metrics.nAsianMajority);
102369
- console.log(" - nNative37to50:", pa.minority.metrics.nNative37to50);
102370
- console.log(" - nNativeMajority:", pa.minority.metrics.nNativeMajority);
102371
- console.log(" - nMinority37to50:", pa.minority.metrics.nMinority37to50);
102372
- console.log(" - nMinorityMajority:", pa.minority.metrics.nMinorityMajority);
102373
- console.log("* Details:");
102374
- console.log(" - VAP:", pa.minority.details.vap);
102375
- console.log(" - comboCategoroes:", pa.minority.details.comboCategories);
102376
- console.log("* Datasets:");
102377
- console.log("- Shapes:", pa.minority.datasets.shapes);
102378
- console.log("- Census:", pa.minority.datasets.census);
102379
- console.log("- VAP:", pa.minority.datasets.vap);
102380
- console.log("- Election:", pa.minority.datasets.election);
102381
- console.log("* Resources:");
102382
- console.log("- N/A");
102383
- console.log("");
102384
- }
102385
- */
102386
104251
  function echoDistrictStatistics(ds) {
102387
104252
  console.log("District Statistics:");
102388
104253
  for (let row of ds.table) {
@@ -102493,6 +104358,17 @@ module.exports = require("assert");
102493
104358
 
102494
104359
  /***/ }),
102495
104360
 
104361
+ /***/ "crypto":
104362
+ /*!*************************!*\
104363
+ !*** external "crypto" ***!
104364
+ \*************************/
104365
+ /*! no static exports found */
104366
+ /***/ (function(module, exports) {
104367
+
104368
+ module.exports = require("crypto");
104369
+
104370
+ /***/ }),
104371
+
102496
104372
  /***/ "fs":
102497
104373
  /*!*********************!*\
102498
104374
  !*** external "fs" ***!