@@ -29,14 +29,17 @@ const MAX_DISTANCE = 130000
29
29
const MAX_TRAVEL_TIME = 120
30
30
const MIN_QUINTILE = 1
31
31
const MAX_QUINTILE = 5
32
+ // stored profile importance is offset by one from MAX_IMPORTANCE
33
+ const PROFILE_MAX_IMPORTANCE = MAX_IMPORTANCE - 1
32
34
33
35
// default to worst, if unknown
34
36
const DEFAULT_EDUCATION_QUINTILE = 5
35
37
const DEFAULT_CRIME_QUINTILE = 5
36
38
37
39
// extra constant weighting always given to travel time over the other two factors
38
- const EXTRA_ACCESS_WEIGHT = 1
40
+ const EXTRA_ACCESS_WEIGHT = 2
39
41
42
+ /* eslint complexity: 0 */
40
43
export default createSelector (
41
44
selectNeighborhoodRoutes ,
42
45
neighborhoodTravelTimes ,
@@ -64,63 +67,89 @@ export default createSelector(
64
67
accessibilityImportance = crimeImportance = schoolsImportance = 1
65
68
}
66
69
67
- // Give accessibility (travel time) extra weighting
70
+ if ( accessibilityImportance === PROFILE_MAX_IMPORTANCE ) {
71
+ // if set to very important, add more extra weight to travel time
72
+ accessibilityImportance += 1
73
+ }
74
+
75
+ // Alwasy give accessibility (travel time) some extra weighting
68
76
accessibilityImportance += EXTRA_ACCESS_WEIGHT
69
77
70
78
const totalImportance = accessibilityImportance + crimeImportance + schoolsImportance
71
79
72
- let accessibilityPercent = accessibilityImportance / totalImportance
73
- let crimePercent = crimeImportance / totalImportance
74
- let schoolPercent = schoolsImportance / totalImportance
75
-
76
80
const neighborhoodsWithRoutes = filter ( neighborhoods . features . map ( ( n , index ) => {
81
+ let accessibilityPercent = accessibilityImportance / totalImportance
82
+ let crimePercent = crimeImportance / totalImportance
83
+ let schoolPercent = schoolsImportance / totalImportance
84
+
77
85
const properties : NeighborhoodProperties = n . properties
78
86
const route = neighborhoodRoutes [ index ]
79
87
const segments = useTransit ? route . routeSegments : [ ]
80
88
const time = useTransit ? travelTimes [ index ] : distanceTime ( origin , n )
81
-
82
- // Map weighting values to percentages
83
-
84
89
// Routable neighborhoods outside the max travel time window will be filtered out.
85
90
// Smaller travel time is better; larger timeWeight is better (reverse range).
86
91
const timeWeight = time < MAX_TRAVEL_TIME ? scale ( time , 0 , MAX_TRAVEL_TIME , 1 , 0 ) : 1
87
-
88
92
// Weight schools either by percentile binned into quarters if given max importance,
89
93
// or otherwise weight by quintile.
90
94
let educationWeight
91
- if ( schoolsImportance === ( MAX_IMPORTANCE - 1 ) ) {
92
- // Group percentile ranking into quarters instead of using quintiles
93
- // if the importance of schools is the max importance.
95
+ if ( schoolsImportance === PROFILE_MAX_IMPORTANCE ) {
96
+ // "very important": instead of quintiles, group percentile ranking into quarters
94
97
const edPercent = properties . education_percentile
95
98
? properties . education_percentile
96
99
: ( DEFAULT_EDUCATION_QUINTILE - 1 ) * 20
97
- const edPercentQuarter = Math . round ( scale ( edPercent , 0 , 100 , 3 , 0 ) )
100
+ let edPercentQuarter = Math . round ( scale ( edPercent , 0 , 100 , 3 , 0 ) )
101
+ // Treat all schools not in the top quarter as being in the bottom quarter
102
+ // to strongly prioritize the top quarter
103
+ if ( edPercentQuarter > 0 ) {
104
+ edPercentQuarter = 3
105
+ }
98
106
educationWeight = scale ( edPercentQuarter , 0 , 3 , 1 , 0 )
99
- } else {
100
- // Use quintiles if the importance of schools is anything less than the max importance.
101
- const educationQuintile = properties . education_percentile_quintile
107
+ } else if ( schoolsImportance > 0 ) {
108
+ // For "somewhat important", prioritize quintile 2 and below
109
+ // For "important", quintile 3 and below (lower is better)
110
+ const prioritizeQuintile = schoolsImportance === 1 ? 2 : 3
111
+ let educationQuintile = properties . education_percentile_quintile
102
112
? properties . education_percentile_quintile
103
113
: DEFAULT_EDUCATION_QUINTILE
104
-
114
+ // Treat all quintiles above the maximum to prioritize as being in the worst quintile
115
+ if ( educationQuintile > prioritizeQuintile ) {
116
+ educationQuintile = MAX_QUINTILE
117
+ }
105
118
// Lowest education quintile is best (reverse range).
106
119
educationWeight = scale ( educationQuintile , MIN_QUINTILE , MAX_QUINTILE , 1 , 0 )
120
+ } else {
121
+ educationWeight = 0 // Not important
107
122
}
108
- let crimeQuintile = properties . violentcrime_quintile
109
- ? properties . violentcrime_quintile : DEFAULT_CRIME_QUINTILE
110
- // Treat lowest two (safest) violent crime quintiles equally
111
- if ( crimeQuintile === 2 ) {
112
- crimeQuintile = 1
113
- }
114
- // Lowest crime quintile is best (reverse range).
115
- const crimeWeight = scale ( crimeQuintile , MIN_QUINTILE , MAX_QUINTILE , 1 , 0 )
116
-
123
+ let crimeWeight = 0
117
124
// Handle missing values (zero in spreadsheet) by re-assigning crime weight
118
- // evenly to the other two factors
119
- if ( properties . violentcrime_quintile === 0 ) {
125
+ // evenly to the other two factors. Also do so if crime set as unimportant.
126
+ if ( properties . violentcrime_quintile === 0 || crimeImportance === 0 ) {
120
127
const halfCrimePercent = crimePercent / 2
121
128
schoolPercent += halfCrimePercent
122
129
accessibilityPercent += halfCrimePercent
123
130
crimePercent = 0
131
+ } else {
132
+ let crimeQuintile = properties . violentcrime_quintile
133
+ ? properties . violentcrime_quintile : DEFAULT_CRIME_QUINTILE
134
+ // Treat lowest two (safest) violent crime quintiles equally
135
+ if ( crimeQuintile === 2 ) {
136
+ crimeQuintile = 1
137
+ }
138
+ if ( crimeImportance === 1 && crimeQuintile < 5 ) {
139
+ // somewhat important; treat all but worst quintile the same
140
+ crimeQuintile = 1
141
+ } else if ( crimeImportance === 2 && crimeQuintile < 4 ) {
142
+ // "important"; treat all but worst two quintiles the same
143
+ crimeQuintile = 1
144
+ } else if ( crimeImportance === PROFILE_MAX_IMPORTANCE && crimeQuintile > 3 ) {
145
+ // "very important"; push results for worst two quintiles to bottom
146
+ // by reassigning weights for those quintiles to be 90% crime
147
+ crimePercent = 0.9
148
+ schoolPercent /= 10
149
+ accessibilityPercent /= 10
150
+ }
151
+ // Lowest crime quintile is best (reverse range).
152
+ crimeWeight = scale ( crimeQuintile , MIN_QUINTILE , MAX_QUINTILE , 1 , 0 )
124
153
}
125
154
126
155
// Calculate weighted overall score from the percentages. Larger score is better.
0 commit comments