@dra2020/district-analytics 1.0.4 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/.prettierrc +5 -0
  2. package/dist/_api.d.ts +27 -0
  3. package/dist/_data.d.ts +130 -0
  4. package/dist/analyze.d.ts +4 -0
  5. package/dist/cli.js +12091 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/cohesive.d.ts +4 -0
  8. package/dist/compact.d.ts +5 -0
  9. package/dist/constants.d.ts +6 -0
  10. package/dist/district-analytics.js +102 -52
  11. package/dist/district-analytics.js.map +1 -0
  12. package/dist/equal.d.ts +4 -0
  13. package/dist/geofeature.d.ts +3 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/minority.d.ts +3 -0
  16. package/dist/political.d.ts +8 -0
  17. package/dist/preprocess.d.ts +2 -0
  18. package/dist/report.d.ts +15 -0
  19. package/dist/settings.d.ts +5 -0
  20. package/dist/test/_cli.d.ts +5 -0
  21. package/dist/types.d.ts +110 -0
  22. package/dist/utils.d.ts +28 -0
  23. package/dist/valid.d.ts +8 -0
  24. package/jestconfig.json +14 -0
  25. package/lib/HelloWorld.d.ts +3 -0
  26. package/lib/HelloWorld.js +11 -0
  27. package/lib/_api.js +91 -0
  28. package/lib/_api.js.map +1 -0
  29. package/lib/_cli.js +434 -0
  30. package/lib/_cli.js.map +1 -0
  31. package/lib/_data.js +425 -0
  32. package/lib/_data.js.map +1 -0
  33. package/lib/analyze.d.ts +3 -0
  34. package/lib/analyze.js +69 -0
  35. package/lib/analyze.js.map +1 -0
  36. package/lib/api.d.ts +34 -0
  37. package/lib/api.js +117 -0
  38. package/lib/api.js.map +1 -0
  39. package/lib/cli.d.ts +1 -0
  40. package/lib/cli.js +386 -0
  41. package/lib/cli.js.map +1 -0
  42. package/lib/cohesive.d.ts +4 -0
  43. package/lib/cohesive.js +132 -0
  44. package/lib/cohesive.js.map +1 -0
  45. package/lib/compact.d.ts +4 -0
  46. package/lib/compact.js +183 -0
  47. package/lib/compact.js.map +1 -0
  48. package/lib/constants.js +367 -0
  49. package/lib/constants.js.map +1 -0
  50. package/lib/data.js +188 -0
  51. package/lib/data.js.map +1 -0
  52. package/lib/equal.d.ts +4 -0
  53. package/lib/equal.js +59 -0
  54. package/lib/equal.js.map +1 -0
  55. package/lib/features.js +19 -0
  56. package/lib/features.js.map +1 -0
  57. package/lib/geofeature.js +112 -0
  58. package/lib/geofeature.js.map +1 -0
  59. package/lib/index.d.ts +5 -0
  60. package/lib/index.js +11 -0
  61. package/lib/index.js.map +1 -0
  62. package/lib/minority.d.ts +3 -0
  63. package/lib/minority.js +61 -0
  64. package/lib/minority.js.map +1 -0
  65. package/lib/political.d.ts +7 -0
  66. package/lib/political.js +88 -0
  67. package/lib/political.js.map +1 -0
  68. package/lib/preprocess.d.ts +4 -0
  69. package/lib/preprocess.js +101 -0
  70. package/lib/preprocess.js.map +1 -0
  71. package/lib/report.d.ts +14 -0
  72. package/lib/report.js +817 -0
  73. package/lib/report.js.map +1 -0
  74. package/lib/sample.d.ts +1 -0
  75. package/lib/sample.js +32 -0
  76. package/lib/sample.js.map +1 -0
  77. package/lib/scorecard.d.ts +4 -0
  78. package/lib/scorecard.js +237 -0
  79. package/lib/scorecard.js.map +1 -0
  80. package/lib/settings.d.ts +5 -0
  81. package/lib/settings.js +18 -0
  82. package/lib/settings.js.map +1 -0
  83. package/lib/types.d.ts +125 -0
  84. package/lib/types.js +7 -0
  85. package/lib/types.js.map +1 -0
  86. package/lib/utils.d.ts +20 -0
  87. package/lib/utils.js +223 -0
  88. package/lib/utils.js.map +1 -0
  89. package/lib/valid.d.ts +5 -0
  90. package/lib/valid.js +230 -0
  91. package/lib/valid.js.map +1 -0
  92. package/main.js +4 -0
  93. package/package.json +2 -7
  94. package/src/_api.ts +121 -0
  95. package/src/_data.ts +595 -0
  96. package/src/analyze.ts +92 -0
  97. package/src/cohesive.ts +156 -0
  98. package/src/compact.ts +208 -0
  99. package/src/constants.ts +371 -0
  100. package/src/equal.ts +75 -0
  101. package/src/geofeature.ts +138 -0
  102. package/src/index.ts +6 -0
  103. package/src/minority.ts +70 -0
  104. package/src/political.ts +114 -0
  105. package/src/preprocess.ts +132 -0
  106. package/src/report.ts +1022 -0
  107. package/src/settings.ts +20 -0
  108. package/src/types.ts +185 -0
  109. package/src/utils.ts +245 -0
  110. package/src/valid.ts +275 -0
  111. package/tsconfig.json +25 -0
  112. package/tslint.json +3 -0
  113. package/types/polygon-clipping/index.d.ts +1 -0
  114. package/webpack.config.js +73 -0
package/lib/utils.js ADDED
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ //
3
+ // UTILITIES
4
+ //
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const S = require("./settings");
7
+ // PLAN HELPERS
8
+ // Is a "neighbor" in state?
9
+ function isInState(geoID) {
10
+ return geoID != S.OUT_OF_STATE;
11
+ }
12
+ exports.isInState = isInState;
13
+ // Is a "neighbor" out of state?
14
+ function isOutOfState(geoID) {
15
+ return geoID == S.OUT_OF_STATE;
16
+ }
17
+ exports.isOutOfState = isOutOfState;
18
+ // Get the districtID to which a geoID is assigned
19
+ function getDistrict(plan, geoID) {
20
+ // All geoIDs in a state *should be* assigned to a district (including the
21
+ // dummy "unassigned" district), but "water-only" features are sometimes missing
22
+ // from a map. This is also a guard against a bug in which a geoID has no district.
23
+ if (keyExists(geoID, plan)) {
24
+ return plan[geoID];
25
+ }
26
+ else {
27
+ return undefined;
28
+ }
29
+ }
30
+ exports.getDistrict = getDistrict;
31
+ // Invert a feature assignment structure to sets of ids by district
32
+ // NOTE - This is here vs. _data.ts, so it can also be used in cli.ts
33
+ function invertPlan(plan) {
34
+ let invertedPlan = {};
35
+ // Add a dummy 'unassigned' district
36
+ invertedPlan[S.NOT_ASSIGNED] = new Set();
37
+ for (let geoID in plan) {
38
+ let districtID = plan[geoID];
39
+ // Make sure the set for the districtID exists
40
+ if (!(objectContains(invertedPlan, districtID))) {
41
+ invertedPlan[districtID] = new Set();
42
+ }
43
+ // Add the geoID to the districtID's set
44
+ invertedPlan[districtID].add(geoID);
45
+ }
46
+ return invertedPlan;
47
+ }
48
+ exports.invertPlan = invertPlan;
49
+ // WORKING WITH GEOIDS
50
+ function parseGeoID(geoID) {
51
+ let parts = {};
52
+ parts['state'] = geoID.substring(0, 2);
53
+ parts['county'] = geoID.substring(0, 5);
54
+ let l = geoID.length;
55
+ if (l >= 11) {
56
+ parts['tract'] = geoID.substring(0, 11);
57
+ }
58
+ if (l >= 12) {
59
+ parts['bg'] = geoID.substring(0, 12);
60
+ }
61
+ if (l == 15) {
62
+ parts['block'] = geoID;
63
+ }
64
+ return parts;
65
+ }
66
+ exports.parseGeoID = parseGeoID;
67
+ function getFIPSFromCountyGeoID(geoID) {
68
+ return geoID.substring(2, 5);
69
+ }
70
+ exports.getFIPSFromCountyGeoID = getFIPSFromCountyGeoID;
71
+ // NORMALIZING RESULTS
72
+ // Convert a raw score [0-1] into a normalized score [0-100]
73
+ function normalize(rawScore, scale, invertp = false) {
74
+ // Invert the axis if necessary to make bigger = better
75
+ if (invertp) {
76
+ rawScore = 1.0 - rawScore;
77
+ }
78
+ // Coerce the value to be w/in the given range
79
+ let rangeMin = scale[0];
80
+ let rangeMax = scale[1];
81
+ let coercedValue = Math.min(Math.max(rawScore, rangeMin), rangeMax);
82
+ // Scale the bounded value w/in the range [0 - (rangeMax - rangeMin)]
83
+ let scaledValue = (coercedValue - rangeMin) / (rangeMax - rangeMin);
84
+ // Finally, make the range [0-100]
85
+ return Math.round(scaledValue * S.NORMALIZED_RANGE);
86
+ }
87
+ exports.normalize = normalize;
88
+ // Round a fractional number [0-1] to the desired level of PRECISION.
89
+ function trim(fullFraction) {
90
+ let shiftPlaces = Math.pow(10, S.PRECISION);
91
+ return Math.round(fullFraction * shiftPlaces) / shiftPlaces;
92
+ }
93
+ exports.trim = trim;
94
+ // ARRAY HELPERS
95
+ function sumArray(arr) {
96
+ return arr.reduce((a, b) => a + b, 0);
97
+ }
98
+ exports.sumArray = sumArray;
99
+ function avgArray(arr) {
100
+ return (arr.reduce((a, b) => a + b, 0)) / arr.length;
101
+ }
102
+ exports.avgArray = avgArray;
103
+ function minArray(arr) {
104
+ return Math.min(...arr);
105
+ }
106
+ exports.minArray = minArray;
107
+ function maxArray(arr) {
108
+ return Math.max(...arr);
109
+ }
110
+ exports.maxArray = maxArray;
111
+ function initArray(n, value) {
112
+ return Array.from(Array(n), () => value);
113
+ }
114
+ exports.initArray = initArray;
115
+ function andArray(arr) {
116
+ return arr.reduce(function (a, b) { return a && b; }, true);
117
+ }
118
+ exports.andArray = andArray;
119
+ // WORKING WITH OBJECT KEYS/PROPERTIES
120
+ // TODO - Terry, is this copesetic?
121
+ // Does an object have a key/property?
122
+ function keyExists(k, o) {
123
+ return k in o;
124
+ }
125
+ exports.keyExists = keyExists;
126
+ // TODO - Terry, can these three be combined into a generic isEmpty() check?
127
+ // Does an object (dict) have any keys/properties?
128
+ function isObjectEmpty(o) {
129
+ return Object.keys(o).length === 0;
130
+ }
131
+ exports.isObjectEmpty = isObjectEmpty;
132
+ // Does a Set have any members?
133
+ function isSetEmpty(s) {
134
+ return s.size === 0;
135
+ }
136
+ exports.isSetEmpty = isSetEmpty;
137
+ // Does an array hold any items?
138
+ function isArrayEmpty(a) {
139
+ if (a === undefined || a.length == 0) {
140
+ // array empty or does not exist
141
+ return true;
142
+ }
143
+ else {
144
+ return false;
145
+ }
146
+ }
147
+ exports.isArrayEmpty = isArrayEmpty;
148
+ // Get the keys for an object
149
+ function getObjectKeys(o) {
150
+ return Object.keys(o);
151
+ }
152
+ exports.getObjectKeys = getObjectKeys;
153
+ // TODO - Convert getNumericObjectKeys() idiom to for..of where possible
154
+ function getNumericObjectKeys(o) {
155
+ return Object.keys(o).map(Number);
156
+ }
157
+ exports.getNumericObjectKeys = getNumericObjectKeys;
158
+ function getSelectObjectKeys(o, v) {
159
+ let selectKeys = [];
160
+ Object.keys(o).forEach(key => {
161
+ if (arrayContains(v, o[key])) {
162
+ selectKeys.push(key);
163
+ }
164
+ });
165
+ return selectKeys;
166
+ }
167
+ exports.getSelectObjectKeys = getSelectObjectKeys;
168
+ function arrayContains(a, item) {
169
+ return a.some(x => x === item);
170
+ }
171
+ exports.arrayContains = arrayContains;
172
+ function objectContains(o, key) {
173
+ return (key in o);
174
+ }
175
+ exports.objectContains = objectContains;
176
+ // ENUM HELPERS
177
+ // Source: https://stackoverflow.com/questions/38034673/determine-the-number-of-enum-elements-typescript
178
+ function countEnumValues(enumName) {
179
+ let count = 0;
180
+ for (let item in enumName) {
181
+ if (isNaN(Number(item)))
182
+ count++;
183
+ }
184
+ return count;
185
+ }
186
+ exports.countEnumValues = countEnumValues;
187
+ // COPYING - Copied from dra-client/util.ts
188
+ function shallowCopy(src) {
189
+ if (Array.isArray(src))
190
+ return src.slice();
191
+ else if (typeof src === 'object') {
192
+ let dst = {};
193
+ for (var p in src)
194
+ if (src.hasOwnProperty(p))
195
+ dst[p] = src[p];
196
+ return dst;
197
+ }
198
+ else
199
+ return src;
200
+ }
201
+ exports.shallowCopy = shallowCopy;
202
+ function deepCopy(src) {
203
+ if (Array.isArray(src)) {
204
+ let dst = [];
205
+ for (let i = 0; i < src.length; i++)
206
+ dst.push(deepCopy(src[i]));
207
+ return dst;
208
+ }
209
+ else if (typeof src === 'object') {
210
+ let dst = {};
211
+ for (var p in src)
212
+ if (src.hasOwnProperty(p))
213
+ dst[p] = deepCopy(src[p]);
214
+ return dst;
215
+ }
216
+ else
217
+ return src;
218
+ }
219
+ exports.deepCopy = deepCopy;
220
+ // TODO - Terry: What is this the simple explanation of what this thing is doing?
221
+ var util_1 = require("@dra2020/util");
222
+ exports.depthof = util_1.depthof;
223
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";AAAA,EAAE;AACF,YAAY;AACZ,EAAE;;AAGF,gCAAgC;AAEhC,eAAe;AAEf,4BAA4B;AAC5B,SAAgB,SAAS,CAAC,KAAa;IACrC,OAAO,KAAK,IAAI,CAAC,CAAC,YAAY,CAAC;AACjC,CAAC;AAFD,8BAEC;AAED,gCAAgC;AAChC,SAAgB,YAAY,CAAC,KAAa;IACxC,OAAO,KAAK,IAAI,CAAC,CAAC,YAAY,CAAC;AACjC,CAAC;AAFD,oCAEC;AAED,kDAAkD;AAClD,SAAgB,WAAW,CAAC,IAAmB,EAAE,KAAa;IAC5D,0EAA0E;IAC1E,gFAAgF;IAChF,mFAAmF;IACnF,IAAI,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;KACpB;SACI;QACH,OAAO,SAAS,CAAC;KAClB;AACH,CAAC;AAVD,kCAUC;AAED,mEAAmE;AACnE,qEAAqE;AACrE,SAAgB,UAAU,CAAC,IAAmB;IAC5C,IAAI,YAAY,GAAG,EAAwB,CAAC;IAE5C,oCAAoC;IACpC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;IAEzC,KAAK,IAAI,KAAK,IAAI,IAAI,EAAE;QACtB,IAAI,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAE7B,8CAA8C;QAC9C,IAAI,CAAC,CAAC,cAAc,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,EAAE;YAC/C,YAAY,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;SACtC;QAED,wCAAwC;QACxC,YAAY,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;KACrC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAnBD,gCAmBC;AAED,sBAAsB;AAEtB,SAAgB,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,GAAiB,EAAE,CAAC;IAE7B,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAExC,IAAI,CAAC,GAAW,KAAK,CAAC,MAAM,CAAC;IAE7B,IAAI,CAAC,IAAI,EAAE,EAAE;QACX,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;KACzC;IAED,IAAI,CAAC,IAAI,EAAE,EAAE;QACX,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;KACtC;IAED,IAAI,CAAC,IAAI,EAAE,EAAE;QACX,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;KACxB;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AArBD,gCAqBC;AAED,SAAgB,sBAAsB,CAAC,KAAa;IAClD,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAFD,wDAEC;AAGD,sBAAsB;AAEtB,4DAA4D;AAC5D,SAAgB,SAAS,CAAC,QAAgB,EAAE,KAAoB,EAAE,UAAmB,KAAK;IACxF,uDAAuD;IACvD,IAAI,OAAO,EAAE;QACX,QAAQ,GAAG,GAAG,GAAG,QAAQ,CAAC;KAC3B;IAED,8CAA8C;IAC9C,IAAI,QAAQ,GAAW,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,QAAQ,GAAW,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IAEpE,qEAAqE;IACrE,IAAI,WAAW,GAAG,CAAC,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;IAEpE,kCAAkC;IAClC,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,gBAAgB,CAAC,CAAC;AACtD,CAAC;AAhBD,8BAgBC;AAED,qEAAqE;AACrE,SAAgB,IAAI,CAAC,YAAoB;IACvC,IAAI,WAAW,GAAG,SAAA,EAAE,EAAI,CAAC,CAAC,SAAS,CAAA,CAAC;IAEpC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC;AAC9D,CAAC;AAJD,oBAIC;AAED,gBAAgB;AAEhB,SAAgB,QAAQ,CAAC,GAAa;IACpC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AACxC,CAAC;AAFD,4BAEC;AAED,SAAgB,QAAQ,CAAC,GAAa;IACpC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;AACvD,CAAC;AAFD,4BAEC;AAED,SAAgB,QAAQ,CAAC,GAAa;IACpC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AAC1B,CAAC;AAFD,4BAEC;AAED,SAAgB,QAAQ,CAAC,GAAa;IACpC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AAC1B,CAAC;AAFD,4BAEC;AAED,SAAgB,SAAS,CAAC,CAAS,EAAE,KAAU;IAC7C,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC;AAFD,8BAEC;AAED,SAAgB,QAAQ,CAAC,GAAc;IACrC,OAAO,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC;AAFD,4BAEC;AAED,sCAAsC;AAEtC,mCAAmC;AACnC,sCAAsC;AACtC,SAAgB,SAAS,CAAC,CAAS,EAAE,CAAS;IAC5C,OAAO,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAFD,8BAEC;AAED,4EAA4E;AAC5E,kDAAkD;AAClD,SAAgB,aAAa,CAAC,CAAS;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAA;AACpC,CAAC;AAFD,sCAEC;AACD,+BAA+B;AAC/B,SAAgB,UAAU,CAAC,CAAM;IAC/B,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAA;AACrB,CAAC;AAFD,gCAEC;AACD,gCAAgC;AAChC,SAAgB,YAAY,CAAC,CAAQ;IACnC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE;QACpC,gCAAgC;QAChC,OAAO,IAAI,CAAC;KACb;SACI;QACH,OAAO,KAAK,CAAC;KACd;AACH,CAAC;AARD,oCAQC;AAED,6BAA6B;AAC7B,SAAgB,aAAa,CAAC,CAAS;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC;AAFD,sCAEC;AAED,wEAAwE;AACxE,SAAgB,oBAAoB,CAAC,CAAS;IAC5C,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAFD,oDAEC;AAED,SAAgB,mBAAmB,CAAC,CAAS,EAAE,CAAQ;IACrD,IAAI,UAAU,GAAa,EAAE,CAAC;IAE9B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC3B,IAAI,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE;YAC5B,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SACtB;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC;AAVD,kDAUC;AAED,SAAgB,aAAa,CAAC,CAAQ,EAAE,IAAS;IAC/C,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AACjC,CAAC;AAFD,sCAEC;AAED,SAAgB,cAAc,CAAC,CAAS,EAAE,GAAQ;IAChD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACpB,CAAC;AAFD,wCAEC;AAGD,eAAe;AAEf,wGAAwG;AACxG,SAAgB,eAAe,CAAC,QAAa;IAC3C,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,IAAI,IAAI,IAAI,QAAQ,EAAE;QACzB,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAAE,KAAK,EAAE,CAAA;KACjC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAND,0CAMC;AAED,2CAA2C;AAE3C,SAAgB,WAAW,CAAC,GAAQ;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QACpB,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC;SAChB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;QAChC,IAAI,GAAG,GAAQ,EAAE,CAAC;QAClB,KAAK,IAAI,CAAC,IAAI,GAAG;YAAE,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;gBAC1C,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,OAAO,GAAG,CAAC;KACZ;;QAEC,OAAO,GAAG,CAAC;AACf,CAAC;AAXD,kCAWC;AAED,SAAgB,QAAQ,CAAC,GAAQ;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QACtB,IAAI,GAAG,GAAU,EAAE,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAW,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;YACzC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,OAAO,GAAG,CAAC;KACZ;SACI,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;QAChC,IAAI,GAAG,GAAQ,EAAE,CAAC;QAClB,KAAK,IAAI,CAAC,IAAI,GAAG;YAAE,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;gBAC1C,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,OAAO,GAAG,CAAC;KACZ;;QAEC,OAAO,GAAG,CAAC;AACf,CAAC;AAhBD,4BAgBC;AAGD,iFAAiF;AACjF,sCAAwC;AAA/B,yBAAA,OAAO,CAAA"}
package/lib/valid.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import * as T from './types';
2
+ import { AnalyticsSession } from './api';
3
+ export declare function doIsComplete(s: AnalyticsSession): T.TestEntry;
4
+ export declare function doIsContiguous(s: AnalyticsSession): T.TestEntry;
5
+ export declare function doIsFreeOfHoles(s: AnalyticsSession): T.TestEntry;
package/lib/valid.js ADDED
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ //
3
+ // MAP/PLAN VALIDATIONS
4
+ //
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const U = require("./utils");
7
+ const S = require("./settings");
8
+ const D = require("./_data");
9
+ //
10
+ // COMPLETE - Are all geo's assigned to a district, and do all districts have
11
+ // at least one geo assigned to them?
12
+ //
13
+ function doIsComplete(s) {
14
+ let test = s.getTest(0 /* Complete */);
15
+ let bAllAssigned = true;
16
+ let bNoneEmpty = true;
17
+ let unassignedFeatures = [];
18
+ let emptyDistricts = [];
19
+ // Get the by district results, including the dummy unassigned district,
20
+ // but ignoring the N+1 summary district
21
+ let bNotEmptyByDistrict = s.districts.statistics[D.DistrictField.bNotEmpty];
22
+ bNotEmptyByDistrict = bNotEmptyByDistrict.slice(0, -1);
23
+ // Are all features assigned to districts?
24
+ // Check the dummy district that holds any unassigned features.
25
+ bAllAssigned = (!bNotEmptyByDistrict[S.NOT_ASSIGNED]);
26
+ if (!bAllAssigned) {
27
+ let unassignedDistrict = s.plan.geoIDsForDistrictID(S.NOT_ASSIGNED);
28
+ unassignedFeatures = Array.from(unassignedDistrict);
29
+ unassignedFeatures = unassignedFeatures.slice(0, S.NUMBER_OF_ITEMS_TO_REPORT);
30
+ }
31
+ // Do all real districts have at least one feature assigned to them?
32
+ bNoneEmpty = U.andArray(bNotEmptyByDistrict.slice(1));
33
+ // Case 1 - One or more districts are missing:
34
+ // The # of enumerated districts minus the dummy NOT_ASSIGNED one should
35
+ // equal the number apportioned districts. This guards against a district
36
+ // not being included in a map that is imported.
37
+ //
38
+ // TODO - I'm no longer checking for this, but DRA should!
39
+ // Case 2 - Or a district is explicitly named but empty:
40
+ // Note, this can happen if a district is created, and then all features
41
+ // are removed from it (in DRA).
42
+ // Populate the test entry
43
+ test['score'] = bAllAssigned && bNoneEmpty;
44
+ if (!bAllAssigned) {
45
+ test['details']['unassignedFeatures'] = unassignedFeatures;
46
+ }
47
+ if (!bNoneEmpty) {
48
+ test['details']['emptyDistricts'] = emptyDistricts;
49
+ }
50
+ // Populate the N+1 summary "district" in district.statistics
51
+ let bNotEmpty = s.districts.statistics[D.DistrictField.bNotEmpty];
52
+ let summaryRow = s.districts.numberOfRows() - 1;
53
+ bNotEmpty[summaryRow] = test['score'];
54
+ return test;
55
+ }
56
+ exports.doIsComplete = doIsComplete;
57
+ //
58
+ // CONTIGUOUS - Is each district in a plan fully connected?
59
+ //
60
+ // NOTE - To check "operational contiguity," we need to use a graph, i.e.,
61
+ // we can't rely on just the geometric contiguity of shapes in a shapefile.
62
+ //
63
+ // To test this, load the NC 2010 map 'SAMPLE-BG-map-discontiguous.csv'.
64
+ //
65
+ function doIsContiguous(s) {
66
+ let test = s.getTest(1 /* Contiguous */);
67
+ // Get the contiguity of each district. Ignore dummy unassigned district
68
+ // and the N+1 summary district.
69
+ let bContiguousByDistrict = s.districts.statistics[D.DistrictField.bContiguous];
70
+ bContiguousByDistrict = bContiguousByDistrict.slice(1, -1);
71
+ // If any real districts aren't contiguous, mark the plan as not contiguous
72
+ let bMapContiguous = U.andArray(bContiguousByDistrict);
73
+ // If the map is not contiguous, log the offending districts.
74
+ let discontiguousDistricts = [];
75
+ let districtID = 1;
76
+ bContiguousByDistrict.forEach(function (bDistrictContiguous) {
77
+ if (!bDistrictContiguous)
78
+ discontiguousDistricts.push(districtID);
79
+ districtID += 1;
80
+ });
81
+ // Populate the test entry
82
+ test['score'] = bMapContiguous;
83
+ if (!bMapContiguous) {
84
+ test['details'] = { 'discontiguousDistricts': discontiguousDistricts };
85
+ }
86
+ // Populate the N+1 summary "district" in district.statistics
87
+ let bContiguous = s.districts.statistics[D.DistrictField.bContiguous];
88
+ let summaryRow = s.districts.numberOfRows() - 1;
89
+ bContiguous[summaryRow] = test['score'];
90
+ return test;
91
+ }
92
+ exports.doIsContiguous = doIsContiguous;
93
+ // Are the features in a district fully connected?
94
+ function isConnected(districtGeos, graph) {
95
+ // export function isConnected(districtGeos: Set<string>, graph: T.ContiguityGraph): boolean {
96
+ // TODO - Terry, why does this constructor need a <T> type specification?
97
+ let visited = new Set();
98
+ let toProcess = [];
99
+ // Start processing with the first geoID in the district
100
+ let iter = districtGeos.values();
101
+ toProcess.push(iter.next().value);
102
+ // While there are geoIDs in the district that haven't been processed
103
+ while (toProcess.length > 0) {
104
+ // Grab a geoID and process it
105
+ let node = toProcess.pop();
106
+ visited.add(node);
107
+ // Get its actual, in-state neighbors
108
+ let actualNeighbors = graph.peerNeighbors(node).filter(x => U.isInState(x));
109
+ // Add neighbors to visit, if they're in the same district Y haven't already been visited
110
+ let neighborsToVisit = actualNeighbors.filter(x => districtGeos.has(x) && (!visited.has(x)));
111
+ // TODO - Terry, is this the quickest/best way to do this?
112
+ toProcess.push(...neighborsToVisit);
113
+ }
114
+ // Stop when you've visited all the geoIDs in the district
115
+ return visited.size == districtGeos.size;
116
+ }
117
+ exports.isConnected = isConnected;
118
+ //
119
+ // FREE OF HOLES - Are any districts fully embedded w/in another district?
120
+ //
121
+ // A district is NOT a "donut hole" district:
122
+ // - If any neighbor is 'OUT_OF_STATE'; or
123
+ // - If there are 2 or more neighboring districts.
124
+ //
125
+ // To test this, load the NC 2010 map 'SAMPLE-BG-map-hole.csv'. District 1,
126
+ // Buncombe County (37021), is a donut hole w/in District 3.
127
+ //
128
+ // TODO - Optimize this to take advantage of district boundary info, if/when
129
+ // we cache one to optimize compactness.
130
+ //
131
+ function doIsFreeOfHoles(s) {
132
+ let test = s.getTest(2 /* FreeOfHoles */);
133
+ // Initialize values
134
+ let bFreeOfHoles = true;
135
+ let embeddedDistricts = [];
136
+ // Get the embeddedness of each district. Ignore dummy unassigned district
137
+ // and the N+1 summary district.
138
+ let bNotEmbeddedByDistrict = s.districts.statistics[D.DistrictField.bNotEmbedded];
139
+ bNotEmbeddedByDistrict = bNotEmbeddedByDistrict.slice(1, -1);
140
+ let districtID = 1;
141
+ bNotEmbeddedByDistrict.forEach(function (bDistrictNotEmbedded) {
142
+ if (!bDistrictNotEmbedded) {
143
+ embeddedDistricts.push(districtID);
144
+ bFreeOfHoles = false;
145
+ }
146
+ districtID += 1;
147
+ });
148
+ // Populate the test entry
149
+ test['score'] = bFreeOfHoles;
150
+ if (!bFreeOfHoles) {
151
+ test['details'] = { 'embeddedDistricts': embeddedDistricts };
152
+ }
153
+ // Populate the N+1 summary "district" in district.statistics
154
+ let bNotEmbedded = s.districts.statistics[D.DistrictField.bNotEmbedded];
155
+ let summaryRow = s.districts.numberOfRows() - 1;
156
+ bNotEmbedded[summaryRow] = test['score'];
157
+ return test;
158
+ }
159
+ exports.doIsFreeOfHoles = doIsFreeOfHoles;
160
+ // Test whether one district is embedded w/in any other.
161
+ function isEmbedded(districtID, geoIDs, plan, graph) {
162
+ // TODO - Make "features" = "geoIDs." These aren't "features" proper, just
163
+ // identifier strings.
164
+ let features = geoIDs;
165
+ let planByGeo = plan.byGeoID();
166
+ // Assume the district is embedded
167
+ let bEmbedded = true;
168
+ // Keep track of the neighoring districts
169
+ let neighboringDistricts = new Set();
170
+ // TODO - Use just the boundary features, when available
171
+ // Get the features for the real district
172
+ let featuresToCheck = Array.from(features);
173
+ // If the district has features, check whether it is embedded
174
+ if (!(U.isArrayEmpty(featuresToCheck))) {
175
+ // For each feature that needs to be checked (see above)
176
+ for (let feature of featuresToCheck) {
177
+ // Get its neighbors (including the virtual "out of state" ones)
178
+ let neighbors = graph.peerNeighbors(feature);
179
+ for (let neighbor of neighbors) {
180
+ if (U.isOutOfState(neighbor)) {
181
+ bEmbedded = false;
182
+ // No need to check any more neighbors
183
+ break;
184
+ }
185
+ else {
186
+ let neighboringDistrict = U.getDistrict(planByGeo, neighbor);
187
+ // Assume that a missing district assignment (= None) means that the
188
+ // feature is "water-only" AND part of the state border (vs. internal)
189
+ // and, therefore, not in the plan/map.
190
+ if (neighboringDistrict == undefined) {
191
+ bEmbedded = false;
192
+ // No need to check any more neighbors
193
+ break;
194
+ }
195
+ else {
196
+ // TODO - Since we're checking *all* features in a district right
197
+ // now, not just boundary features and neighbors in other districts,
198
+ // prune out the current district. If/when we optimize compactness
199
+ // to cache district boundaries (as before in my Python implementation),
200
+ // we won't have to guard adding "neighboring" districts in this way.
201
+ if (neighboringDistrict != districtID) {
202
+ neighboringDistricts.add(neighboringDistrict);
203
+ }
204
+ if (neighboringDistricts.size > 1) {
205
+ bEmbedded = false;
206
+ // No need to check any more neighbors
207
+ break;
208
+ }
209
+ }
210
+ }
211
+ }
212
+ // If a district is not embedded, there's no need to check anymore
213
+ // border geos.
214
+ if (!bEmbedded) {
215
+ break;
216
+ }
217
+ }
218
+ }
219
+ return bEmbedded;
220
+ }
221
+ exports.isEmbedded = isEmbedded;
222
+ // TODO - MIXED MAPS
223
+ // - When we generalize to mixed maps, the determination of "neighbors in
224
+ // the map" -- which is the core function in determining connectedness -- becomes
225
+ // *much* more complicated and dynamic.
226
+ // - I can write up (if not implement) the logic for this. It is a bit tricky
227
+ // and requires special preprocessing of the summary level hierarchy (which I
228
+ // also have a script for that we can repurpose) to distinguish between 'interior'
229
+ // and 'edge' children.
230
+ //# sourceMappingURL=valid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid.js","sourceRoot":"","sources":["../src/valid.ts"],"names":[],"mappings":"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"}
package/main.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Invoke the CLI
4
+ var run = require('./dist/cli.js');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dra2020/district-analytics",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "District analytics TypeScript package",
5
5
  "main": "dist/district-analytics.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,11 +16,6 @@
16
16
  "type": "git",
17
17
  "url": "git+https://github.com/dra2020/district-analytics.git"
18
18
  },
19
- "files": [
20
- "*/src/*",
21
- "*/dist/*.*",
22
- "*/dist/*/*.*"
23
- ],
24
19
  "keywords": [
25
20
  "Typescript"
26
21
  ],
@@ -45,7 +40,7 @@
45
40
  "tslint": "^5.18.0",
46
41
  "tslint-config-prettier": "^1.18.0",
47
42
  "typescript": "^3.5.3",
48
- "webpack": "^4.39.2",
43
+ "webpack": "^4.39.3",
49
44
  "webpack-cli": "^3.3.7",
50
45
  "yargs": "^12.0.5"
51
46
  },
package/src/_api.ts ADDED
@@ -0,0 +1,121 @@
1
+ //
2
+ // THE NODE PACKAGE API
3
+ //
4
+
5
+ import { doPreprocessData } from './preprocess'
6
+ import { doAnalyzePlan, doAnalyzeDistricts } from './analyze'
7
+ import {
8
+ doConfigureScales, doAnalyzePostProcessing, doPrepareScorecard,
9
+ doPrepareTestLog, Scorecard
10
+ } from './report'
11
+
12
+ import * as D from './_data';
13
+ import * as T from './types'
14
+ import * as U from './utils';
15
+ import * as S from './settings';
16
+
17
+ export class AnalyticsSession {
18
+ title: string;
19
+ legislativeDistricts: boolean;
20
+ config: T.Dict = {};
21
+
22
+ bOneTimeProcessingDone = false;
23
+ bPlanAnalyzed = false;
24
+ bPostProcessingDone = false;
25
+ testScales = {} as T.TestScales;
26
+ tests = {} as T.TestEntries;
27
+ scorecard = {} as Scorecard;
28
+
29
+ state: D.State;
30
+ counties: D.Counties;
31
+ graph: D.Graph;
32
+ features: D.Features;
33
+ plan: D.Plan;
34
+ districts: D.Districts;
35
+
36
+ constructor(SessionRequest: T.SessionRequest) {
37
+ this.title = SessionRequest['title'];
38
+ this.legislativeDistricts = SessionRequest['legislativeDistricts'];
39
+ this.config = this.processConfig(SessionRequest['config'])
40
+
41
+ this.state = new D.State(this, SessionRequest['stateXX'], SessionRequest['nDistricts']);
42
+ this.counties = new D.Counties(this, SessionRequest['counties']);
43
+ this.graph = new D.Graph(this, SessionRequest['graph']);
44
+ this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
45
+ this.plan = new D.Plan(this, SessionRequest['plan']);
46
+ this.districts = new D.Districts(this, SessionRequest['districtShapes']);
47
+
48
+ // NOTE: I've pulled these out of the individual analytics to here. Eventually,
49
+ // we could want them to passed into an analytics session as data, along with
50
+ // everything else. For now, this keeps branching out of the main code.
51
+ doConfigureScales(this);
52
+ }
53
+ processConfig(config: T.Dict): T.Dict {
54
+ // NOTE - Session settings are required:
55
+ // - Analytics suites can be defaulted to all with [], but
56
+ // - Dataset keys must be explicitly specified with 'dataset'
57
+
58
+ let defaultSuites = [T.Suite.Legal, T.Suite.Fair, T.Suite.Best];
59
+
60
+ // If the config passed in has no suites = [], use the default suites
61
+ if (U.isArrayEmpty(config['suites'])) {
62
+ config['suites'] = defaultSuites;
63
+ }
64
+
65
+ // Default the Census & redistricting cycle to 2010
66
+ if (!(U.keyExists('cycle', config))) config['cycle'] = 2010;
67
+
68
+ return config;
69
+ }
70
+
71
+ // Using the the data in the analytics session, calculate all the
72
+ // analytics & validations, saving/updating the individual test results.
73
+ analyzePlan(bLog: boolean = false): boolean {
74
+ try {
75
+ doPreprocessData(this);
76
+ doAnalyzeDistricts(this, bLog);
77
+ doAnalyzePlan(this, bLog);
78
+ doAnalyzePostProcessing(this);
79
+ }
80
+ catch {
81
+ console.log("Exception caught by analyzePlan()");
82
+ return false;
83
+ }
84
+
85
+ return true;
86
+ }
87
+
88
+ // Get an individual test, so you can drive UI with the results.
89
+ getTest(testID: T.Test): T.TestEntry {
90
+ // Get the existing test entries
91
+ // T.Test is a numeric enum, so convert the string keys to numbers
92
+ let testValues = U.getNumericObjectKeys(this.tests);
93
+
94
+ // If there's no entry for this test, create & initialize one
95
+ if (!(testValues.includes(testID))) {
96
+ this.tests[testID] = {} as T.TestEntry;
97
+ this.tests[testID]['score'] = undefined;
98
+ this.tests[testID]['details'] = {};
99
+ }
100
+
101
+ // Return a pointer to the the test entry for this test
102
+ return this.tests[testID];
103
+ }
104
+
105
+ // Prepare a scorecard for rendering
106
+ // NOTE - This assumes that analyzePlan() has been run!
107
+ prepareScorecard(): any {
108
+ return doPrepareScorecard(this);
109
+ }
110
+
111
+ // Prepare test results for rendering
112
+ // NOTE - This assumes that analyzePlan() has been run!
113
+ prepareTestLog(): any {
114
+ return doPrepareTestLog(this);
115
+ }
116
+
117
+ populationDeviationThreshold(): number {
118
+ return 1 - this.testScales[T.Test.PopulationDeviation]['testScale'][0];
119
+ }
120
+ }
121
+