@dra2020/district-analytics 1.0.11 → 2.0.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 +77 -65
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +73 -60
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -148,7 +148,7 @@ class AnalyticsSession {
|
|
|
148
148
|
// NOTE - Session settings are required:
|
|
149
149
|
// - Analytics suites can be defaulted to all with [], but
|
|
150
150
|
// - Dataset keys must be explicitly specified with 'dataset'
|
|
151
|
-
// TODO - Remove this mechanism. Always run everything.
|
|
151
|
+
// TODO - DASHBOARD: Remove this mechanism. Always run everything.
|
|
152
152
|
let defaultSuites = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
153
153
|
// If the config passed in has no suites = [], use the default suites
|
|
154
154
|
if (U.isArrayEmpty(config['suites'])) {
|
|
@@ -328,10 +328,10 @@ class Districts {
|
|
|
328
328
|
}
|
|
329
329
|
// This is the workhorse computational routine!
|
|
330
330
|
//
|
|
331
|
-
// TODO -
|
|
332
|
-
// TODO -
|
|
331
|
+
// TODO - OPTIMIZE for getting multiple properties from the same feature?
|
|
332
|
+
// TODO - OPTIMIZE by only re-calc'ing districts that have changed?
|
|
333
333
|
// In this case, special attention to getting county-splits right.
|
|
334
|
-
// TODO -
|
|
334
|
+
// TODO - OPTIMIZE by async'ing this?
|
|
335
335
|
// TODO - Is there a way to do this programmatically off data? Does it matter?
|
|
336
336
|
recalcStatistics(bLog = false) {
|
|
337
337
|
// Compute these once per recalc cycle
|
|
@@ -340,7 +340,7 @@ class Districts {
|
|
|
340
340
|
let planByDistrict = this._session.plan.byDistrictID();
|
|
341
341
|
let plan = this._session.plan;
|
|
342
342
|
let graph = this._session.graph;
|
|
343
|
-
//
|
|
343
|
+
// NOTE - SPLITTING
|
|
344
344
|
// Add an extra 0th virtual county bucket for county-district splitting analysis
|
|
345
345
|
let nCountyBuckets = this._session.counties.nCounties + 1;
|
|
346
346
|
// INITIALIZE STATE VALUES THAT WILL BE ACCUMULATED
|
|
@@ -366,7 +366,7 @@ class Districts {
|
|
|
366
366
|
// INITIALIZE DISTRICT VALUES THAT WILL BE ACCUMULATED (VS. DERIVED)
|
|
367
367
|
let featurePop;
|
|
368
368
|
let totalPop = 0;
|
|
369
|
-
//
|
|
369
|
+
// NOTE - SPLITTING
|
|
370
370
|
let countySplits = U.initArray(nCountyBuckets, 0);
|
|
371
371
|
let demVotes = 0;
|
|
372
372
|
let repVotes = 0;
|
|
@@ -397,31 +397,37 @@ class Districts {
|
|
|
397
397
|
// Map from geoID to feature index
|
|
398
398
|
let featureID = outerThis._session.features.featureID(geoID);
|
|
399
399
|
let f = outerThis._session.features.featureByIndex(featureID);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
400
|
+
if (f == undefined) {
|
|
401
|
+
console.log("Skipping undefined feature in district statistics: GEOID =", geoID, "Feature ID =", featureID);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
// ACCUMULATE VALUES
|
|
405
|
+
// Total population of each feature
|
|
406
|
+
// NOTE - This result is used more than once
|
|
407
|
+
featurePop = outerThis._session.features.fieldForFeature(f, "CENSUS" /* CENSUS */, "Tot" /* TotalPop */);
|
|
408
|
+
// Total district population
|
|
409
|
+
totalPop += featurePop;
|
|
410
|
+
// NOTE - SPLITTING
|
|
411
|
+
// Total population by counties w/in a district,
|
|
412
|
+
// except the dummy unassigned district 0
|
|
413
|
+
if (i > 0)
|
|
414
|
+
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
415
|
+
// Democratic and Republican vote totals
|
|
416
|
+
demVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "D" /* DemVotes */);
|
|
417
|
+
repVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "R" /* RepVotes */);
|
|
418
|
+
// Voting-age demographic breakdowns (or citizen voting-age)
|
|
419
|
+
totalVAP += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Tot" /* TotalPop */);
|
|
420
|
+
whitePop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Wh" /* WhitePop */);
|
|
421
|
+
blackPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "BlC" /* BlackPop */);
|
|
422
|
+
hispanicPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "His" /* HispanicPop */);
|
|
423
|
+
pacificPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "PacC" /* PacificPop */);
|
|
424
|
+
asianPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "AsnC" /* AsianPop */);
|
|
425
|
+
nativePop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "NatC" /* NativePop */);
|
|
426
|
+
// 4 - MORE ...
|
|
427
|
+
}
|
|
423
428
|
}
|
|
424
|
-
|
|
429
|
+
else
|
|
430
|
+
console.log("Skipping water-only feature in district statistics:", geoID);
|
|
425
431
|
});
|
|
426
432
|
// COMPUTE DERIVED VALUES
|
|
427
433
|
// Population deviation % and equal population (boolean) by district.
|
|
@@ -619,6 +625,8 @@ exports.Features = Features;
|
|
|
619
625
|
// f is a direct GeoJSON feature
|
|
620
626
|
// p is a geoID
|
|
621
627
|
function _getFeatures(f, datasetKey, p) {
|
|
628
|
+
// Echo parameters for debugging
|
|
629
|
+
// console.log("f =", f, "k = ", datasetKey, "p =", p);
|
|
622
630
|
// Shim to load sample data2.json from disk for command-line scaffolding
|
|
623
631
|
if (f.properties && f.properties['datasets']) {
|
|
624
632
|
return f.properties['datasets'][datasetKey][p];
|
|
@@ -705,7 +713,7 @@ class Plan {
|
|
|
705
713
|
// return newPlan;
|
|
706
714
|
// }
|
|
707
715
|
invertPlan() {
|
|
708
|
-
//
|
|
716
|
+
// NOTE - UNASSIGNED
|
|
709
717
|
this._planByDistrictID = invertPlan(this._planByGeoID, this._session);
|
|
710
718
|
this.districtIDs = U.getNumericObjectKeys(this._planByDistrictID);
|
|
711
719
|
}
|
|
@@ -721,8 +729,8 @@ function invertPlan(plan, s) {
|
|
|
721
729
|
let invertedPlan = {};
|
|
722
730
|
// Add a dummy 'unassigned' district
|
|
723
731
|
invertedPlan[S.NOT_ASSIGNED] = new Set();
|
|
724
|
-
//
|
|
725
|
-
//
|
|
732
|
+
// NOTE - UNASSIGNED
|
|
733
|
+
// The feature assignments coming from DRA do not include unassigned ones.
|
|
726
734
|
// - In the DRA-calling context, there's an analytics session with a reference
|
|
727
735
|
// to the features. Loop over all the features to find the unassigned ones,
|
|
728
736
|
// and add them to the dummy unassigned district explicitly.
|
|
@@ -735,7 +743,7 @@ function invertPlan(plan, s) {
|
|
|
735
743
|
// to the dummy unassigned district 0.
|
|
736
744
|
if (!(U.keyExists(geoID, plan)))
|
|
737
745
|
invertedPlan[S.NOT_ASSIGNED].add(geoID);
|
|
738
|
-
//
|
|
746
|
+
// NOTE - NOT skipping WATER-ONLY features here, because we're
|
|
739
747
|
// not skipping them below when they are explicitly assigned in plans. Should
|
|
740
748
|
// we skip them in both places?
|
|
741
749
|
}
|
|
@@ -762,7 +770,13 @@ class Graph {
|
|
|
762
770
|
peerNeighbors(node) {
|
|
763
771
|
// Get the neighboring geoIDs connected to a geoID
|
|
764
772
|
// Ignore the lengths of the shared borders (the values), for now
|
|
765
|
-
|
|
773
|
+
// Protect against getting a GEOID that's not in the graph
|
|
774
|
+
if (U.keyExists(node, this._graph)) {
|
|
775
|
+
return U.getObjectKeys(this._graph[node]);
|
|
776
|
+
}
|
|
777
|
+
else
|
|
778
|
+
return [];
|
|
779
|
+
// return U.getObjectKeys(this._graph[node]);
|
|
766
780
|
}
|
|
767
781
|
}
|
|
768
782
|
exports.Graph = Graph;
|
|
@@ -1505,7 +1519,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1505
1519
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1506
1520
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1507
1521
|
// CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
|
|
1508
|
-
// TODO -
|
|
1522
|
+
// TODO - POLY: Confirm Cartesian calculations
|
|
1509
1523
|
function gfArea(poly) {
|
|
1510
1524
|
let area = _polygonArea(poly);
|
|
1511
1525
|
return area;
|
|
@@ -1540,7 +1554,7 @@ function _polygonSimpleArea(p) {
|
|
|
1540
1554
|
// Generalizes the above for MultiPolygons -- cloned from polyArea() in 'poly'
|
|
1541
1555
|
function _polygonArea(poly) {
|
|
1542
1556
|
let polyOptions = { noLatitudeCorrection: true }; // NO-OP?
|
|
1543
|
-
poly = Poly.polyNormalize(poly, polyOptions); // TODO - Discuss w/ Terry
|
|
1557
|
+
poly = Poly.polyNormalize(poly, polyOptions); // TODO - POLY: Discuss w/ Terry
|
|
1544
1558
|
let a = 0;
|
|
1545
1559
|
// A MultiPolygon is a set of polygons
|
|
1546
1560
|
for (let i = 0; poly && i < poly.length; i++) {
|
|
@@ -1553,7 +1567,7 @@ function _polygonArea(poly) {
|
|
|
1553
1567
|
}
|
|
1554
1568
|
return a;
|
|
1555
1569
|
}
|
|
1556
|
-
// TODO -
|
|
1570
|
+
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1557
1571
|
// The perimeter calculation already just computes cartesian distance if you
|
|
1558
1572
|
// pass in the noLatitudeCorrection flag. You would need to divide by
|
|
1559
1573
|
// Poly.EARTH_RADIUS to go from the returned units of meters to Lat/Lon “units”.
|
|
@@ -1562,7 +1576,7 @@ function gfPerimeter(poly) {
|
|
|
1562
1576
|
return perimeter;
|
|
1563
1577
|
}
|
|
1564
1578
|
exports.gfPerimeter = gfPerimeter;
|
|
1565
|
-
// TODO -
|
|
1579
|
+
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1566
1580
|
// Cloned from polyPerimeter() in 'poly' and revised to use Cartesian distance
|
|
1567
1581
|
// NOTE: No conversion of degrees to radians!
|
|
1568
1582
|
function _polygonPerimeter(poly) {
|
|
@@ -1586,7 +1600,7 @@ function _distance(x1, y1, x2, y2) {
|
|
|
1586
1600
|
d = Math.sqrt((dLat * dLat) + (dLon * dLon));
|
|
1587
1601
|
return d;
|
|
1588
1602
|
}
|
|
1589
|
-
// TODO -
|
|
1603
|
+
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1590
1604
|
// As I mentioned, the polyCircle code was already just treating the coordinate
|
|
1591
1605
|
// system as Cartesian. I then did polyFromCircle to convert it to a polygon that
|
|
1592
1606
|
// then could be passed to polyArea in order to take into account the projection.
|
|
@@ -1620,6 +1634,7 @@ function __export(m) {
|
|
|
1620
1634
|
}
|
|
1621
1635
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1622
1636
|
__export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
|
|
1637
|
+
__export(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1623
1638
|
__export(__webpack_require__(/*! ./results */ "./src/results.ts"));
|
|
1624
1639
|
__export(__webpack_require__(/*! ./types */ "./src/types.ts"));
|
|
1625
1640
|
|
|
@@ -1795,7 +1810,7 @@ function doPreprocessData(s, bLog = false) {
|
|
|
1795
1810
|
doPreprocessElection(s, bLog);
|
|
1796
1811
|
s.bOneTimeProcessingDone = true;
|
|
1797
1812
|
}
|
|
1798
|
-
//
|
|
1813
|
+
// NOTE - UNASSIGNED: Made both the planByGeoID & DistrictID are right
|
|
1799
1814
|
// Invert the plan by district ID
|
|
1800
1815
|
s.plan.invertPlan();
|
|
1801
1816
|
// Create a map of geoIDs to feature IDs
|
|
@@ -1847,7 +1862,7 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1847
1862
|
let fipsCodes = U.getObjectKeys(totalByCounty);
|
|
1848
1863
|
// Sort the results
|
|
1849
1864
|
fipsCodes = fipsCodes.sort();
|
|
1850
|
-
//
|
|
1865
|
+
// NOTE - SPLITTING
|
|
1851
1866
|
// Add a dummy county, for county-district splitting analysis
|
|
1852
1867
|
fipsCodes.unshift('000');
|
|
1853
1868
|
// Create the ID-ordinal map
|
|
@@ -1878,7 +1893,7 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1878
1893
|
// Loop over the counties
|
|
1879
1894
|
for (let county in fipsCodes) {
|
|
1880
1895
|
let fipsCode = fipsCodes[county];
|
|
1881
|
-
//
|
|
1896
|
+
// NOTE - SPLITTING
|
|
1882
1897
|
// Skip the dummy county
|
|
1883
1898
|
if (fipsCode == '000')
|
|
1884
1899
|
continue;
|
|
@@ -1936,8 +1951,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
1936
1951
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1937
1952
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
1938
1953
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1939
|
-
// TODO - DASHBOARD: Delete
|
|
1940
|
-
// import { doAnalyzePostProcessing } from './report'
|
|
1941
1954
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
1942
1955
|
const state_reqs_json_1 = __importDefault(__webpack_require__(/*! ../static/state-reqs.json */ "./static/state-reqs.json"));
|
|
1943
1956
|
// Example
|
|
@@ -1988,12 +2001,12 @@ let sampleSplitting = {
|
|
|
1988
2001
|
],
|
|
1989
2002
|
unexpectedAffected: 0.3096,
|
|
1990
2003
|
nSplitVTDs: 12,
|
|
1991
|
-
splitVTDs: []
|
|
2004
|
+
splitVTDs: ["VTD-01", "VTD-02", "VTD-03", "VTD-04", "VTD-05", "VTD-06", "VTD-07", "VTD-08", "VTD-09", "VTD-10", "VTD-11", "VTD-12"]
|
|
1992
2005
|
},
|
|
1993
2006
|
datasets: {},
|
|
1994
2007
|
resources: {}
|
|
1995
2008
|
};
|
|
1996
|
-
// TODO - This category is still being fleshed out.
|
|
2009
|
+
// TODO - PARTISAN: This category is still being fleshed out.
|
|
1997
2010
|
let samplePartisan = {
|
|
1998
2011
|
score: 100,
|
|
1999
2012
|
metrics: {
|
|
@@ -2008,7 +2021,7 @@ let samplePartisan = {
|
|
|
2008
2021
|
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2009
2022
|
}
|
|
2010
2023
|
};
|
|
2011
|
-
// TODO - This category is still being fleshed out.
|
|
2024
|
+
// TODO - MINORITY: This category is still being fleshed out.
|
|
2012
2025
|
let sampleMinority = {
|
|
2013
2026
|
score: null,
|
|
2014
2027
|
metrics: {
|
|
@@ -2126,9 +2139,9 @@ function preparePlanAnalytics(s, bLog = false) {
|
|
|
2126
2139
|
// None at this time
|
|
2127
2140
|
},
|
|
2128
2141
|
datasets: {
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
// shapes:
|
|
2142
|
+
// NOTE - DATASETS
|
|
2143
|
+
shapes: U.deepCopy(s.config['descriptions']['SHAPES'])
|
|
2144
|
+
// shapes: "2010 VTD shapes"
|
|
2132
2145
|
},
|
|
2133
2146
|
resources: {
|
|
2134
2147
|
// None at this time
|
|
@@ -2379,7 +2392,7 @@ const polsbyPopperDefn = {
|
|
|
2379
2392
|
externalType: TestType.Number,
|
|
2380
2393
|
suites: [2 /* Best */]
|
|
2381
2394
|
};
|
|
2382
|
-
//
|
|
2395
|
+
// NOTE - SPLITTING
|
|
2383
2396
|
const unexpectedCountySplitsDefn = {
|
|
2384
2397
|
ID: 7 /* UnexpectedCountySplits */,
|
|
2385
2398
|
name: "Unexpected County Splits",
|
|
@@ -2387,7 +2400,7 @@ const unexpectedCountySplitsDefn = {
|
|
|
2387
2400
|
externalType: TestType.Percentage,
|
|
2388
2401
|
suites: [2 /* Best */]
|
|
2389
2402
|
};
|
|
2390
|
-
//
|
|
2403
|
+
// NOTE - SPLITTING
|
|
2391
2404
|
const VTDSplitsDefn = {
|
|
2392
2405
|
ID: 10 /* VTDSplits */,
|
|
2393
2406
|
name: "VTD Splits",
|
|
@@ -2395,7 +2408,7 @@ const VTDSplitsDefn = {
|
|
|
2395
2408
|
externalType: TestType.Number,
|
|
2396
2409
|
suites: [2 /* Best */]
|
|
2397
2410
|
};
|
|
2398
|
-
//
|
|
2411
|
+
// NOTE - SPLITTING
|
|
2399
2412
|
const countySplittingDefn = {
|
|
2400
2413
|
ID: 8 /* CountySplitting */,
|
|
2401
2414
|
name: "County Splitting",
|
|
@@ -2403,7 +2416,7 @@ const countySplittingDefn = {
|
|
|
2403
2416
|
externalType: TestType.Number,
|
|
2404
2417
|
suites: [2 /* Best */]
|
|
2405
2418
|
};
|
|
2406
|
-
//
|
|
2419
|
+
// NOTE - SPLITTING
|
|
2407
2420
|
const districtSplittingDefn = {
|
|
2408
2421
|
ID: 9 /* DistrictSplitting */,
|
|
2409
2422
|
name: "District Splitting",
|
|
@@ -2427,7 +2440,7 @@ const testDefns = {
|
|
|
2427
2440
|
[4 /* PopulationDeviation */]: populationDeviationDefn,
|
|
2428
2441
|
[5 /* Reock */]: reockDefn,
|
|
2429
2442
|
[6 /* PolsbyPopper */]: polsbyPopperDefn,
|
|
2430
|
-
//
|
|
2443
|
+
// NOTE - SPLITTING
|
|
2431
2444
|
[7 /* UnexpectedCountySplits */]: unexpectedCountySplitsDefn,
|
|
2432
2445
|
[10 /* VTDSplits */]: VTDSplitsDefn,
|
|
2433
2446
|
[8 /* CountySplitting */]: countySplittingDefn,
|
|
@@ -2455,7 +2468,7 @@ function doConfigureScales(s) {
|
|
|
2455
2468
|
s.testScales[6 /* PolsbyPopper */] = { scale: [0.10, 0.50] };
|
|
2456
2469
|
const nDistricts = s.state.nDistricts;
|
|
2457
2470
|
const nCounties = s.counties.nCounties;
|
|
2458
|
-
//
|
|
2471
|
+
// NOTE - SPLITTING: Experiment w/ this multiplier. Only allowing the expected
|
|
2459
2472
|
// number of county splits seems too stringent, empirically.
|
|
2460
2473
|
const allowableCountySplitsMultiplier = 1.5;
|
|
2461
2474
|
const nAllowableSplits = Math.min(allowableCountySplitsMultiplier * (nDistricts - 1));
|
|
@@ -2514,7 +2527,7 @@ exports.PRECISION = 4;
|
|
|
2514
2527
|
exports.NORMALIZED_RANGE = 100;
|
|
2515
2528
|
// The dummy district ID for features not assigned districts yet
|
|
2516
2529
|
exports.NOT_ASSIGNED = 0;
|
|
2517
|
-
// TODO -
|
|
2530
|
+
// TODO - DASHBOARD: Discuss w/ Dave
|
|
2518
2531
|
// # of items to report as problematic (e.g., features, districts, etc.)
|
|
2519
2532
|
exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
|
|
2520
2533
|
// The virtual geoID for "neighbors" in other states
|
|
@@ -2579,7 +2592,7 @@ function isOutOfState(geoID) {
|
|
|
2579
2592
|
return geoID == S.OUT_OF_STATE;
|
|
2580
2593
|
}
|
|
2581
2594
|
exports.isOutOfState = isOutOfState;
|
|
2582
|
-
//
|
|
2595
|
+
// NOTE - UNASSIGNED
|
|
2583
2596
|
// Get the districtID to which a geoID is assigned
|
|
2584
2597
|
function getDistrict(plan, geoID) {
|
|
2585
2598
|
// All geoIDs in a state *should be* assigned to a district (including the
|
|
@@ -2637,7 +2650,7 @@ function normalize(rawScore, testScale) {
|
|
|
2637
2650
|
let coercedValue = Math.min(Math.max(rawScore, rangeMin), rangeMax);
|
|
2638
2651
|
// Scale the bounded value w/in the range [0 - (rangeMax - rangeMin)]
|
|
2639
2652
|
let scaledValue = (coercedValue - rangeMin) / (rangeMax - rangeMin);
|
|
2640
|
-
//
|
|
2653
|
+
// NOTE - SPLITTING
|
|
2641
2654
|
// Invert the scaled value if necessary to make bigger = better
|
|
2642
2655
|
if (testScale.bInvertScaled) {
|
|
2643
2656
|
scaledValue = 1.0 - scaledValue;
|