-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
labelSchema.js
294 lines (262 loc) · 9.92 KB
/
labelSchema.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
var _ = require('lodash');
const removeAccents = require('remove-accents');
// lowercase characters and remove some punctuation
function normalizeString(str){
if (!str) {
return '';
}
return removeAccents(str.toLowerCase().split(/[ ,-]+/).join(' '));
}
// French Guiana, Guadeloupe, Martinique, Reunion, Mayotte
const FRA_OVERSEAS = ['GF', 'GP', 'MQ', 'RE', 'YT'];
// find the first field of record that has a non-empty value that's not already in labelParts
function getFirstProperty(fields) {
return function(record) {
for (var i = 0; i < fields.length; i++) {
var fieldValue = record[fields[i]];
if (!_.isEmpty(fieldValue)) {
return fieldValue[0];
}
}
};
}
// this function is exclusively used for figuring out which field to use for states/provinces
// 1. if the record belongs to a dependency, skip the region, eg - San Juan, PR
// 2. if a state/province is the most granular bit of info entered, the label should contain
// the full state/province name, eg: Pennsylvania, USA and Ontario, CA
// 3. otherwise, the state/province abbreviation should be used, eg: Lancaster, PA, USA and Bruce, ON, CA
// 4. if the abbreviation isn't available, use the full state/province name
function getRegionalValue(record) {
if (!_.isEmpty(record.dependency) || !_.isEmpty(record.dependency_a)) {
return;
}
if ('region' === record.layer && !_.isEmpty(record.region)) {
// return full state name when state is the most granular piece of info
return record.region[0];
} else if (!_.isEmpty(record.region_a)) {
// otherwise just return the region code when available
return record.region_a[0];
} else if (!_.isEmpty(record.region)) {
// return the full name when there's no region code available
return record.region[0];
}
}
// The same as getRegionalValue above, but only returns a region if the region name
// is distinct from the locality/localadmin/city name
// This works best for large cities in countries where the region name/abbr is not _always_ included in the label
function getUniqueRegionalValue(record) {
if (!_.isEmpty(record.dependency) || !_.isEmpty(record.dependency_a)) {
return;
}
// handle the region value where this record itself is a region
if ('region' === record.layer) {
if (!_.isEmpty(record.region)) {
// return full state name when state is the most granular piece of info
return record.region[0];
}
} else {
const localityValue = getFirstProperty(['locality', 'localadmin'])(record);
if (record.region && normalizeString(localityValue) === normalizeString(record.region[0])) {
// skip returning anything when the region and locality name are identical
// This handles major cities in their own region like Berlin, Tokyo, Paris, Sao Paulo, etc
return;
}
// prefer the region abbreviation, fall back to the region name if no abbreviation
if (!_.isEmpty(record.region_a)) {
return record.region_a[0];
} else if (!_.isEmpty(record.region)) {
return record.region[0];
}
}
}
// this function generates the last field of the labels for US records
// 1. use dependency name if layer is dependency, eg - Puerto Rico
// 2. use country name if layer is country, eg - United States
// 3. use dependency abbreviation if applicable, eg - San Juan, PR
// 4. use dependency name if no abbreviation, eg - San Juan, Puerto Rico
// 5. use country abbreviation, eg - Lancaster, PA, USA
function getUSADependencyOrCountryValue(record) {
if ('dependency' === record.layer && !_.isEmpty(record.dependency)) {
return record.dependency[0];
} else if ('country' === record.layer && !_.isEmpty(record.country)) {
return record.country[0];
}
if (!_.isEmpty(record.dependency_a)) {
return record.dependency_a[0];
} else if (!_.isEmpty(record.dependency)) {
return record.dependency[0];
}
return record.country_a[0];
}
// this function generates the last field of the labels for FRA records
// 1. use the region name if the record is a in the French overseas, eg - Saint-Denis, Reunion
// 2. use dependency name if not null
// 3. use country name, eg - Paris, France
function getFRACountryValue() {
const _overseas = getFirstProperty(['region', 'dependency', 'country']);
const _default = getFirstProperty(['dependency', 'country']);
return (record) => {
if (!_.isEmpty(record.region_a) && _.includes(FRA_OVERSEAS, record.region_a[0])) {
return _overseas(record);
}
return _default(record);
};
}
function isInNYC(record) {
const _region_a = getFirstProperty(['region_a'])(record);
const _country_a = getFirstProperty(['country_a'])(record);
const _locality_a = getFirstProperty(['locality_a'])(record);
return _country_a === 'USA' && _region_a === 'NY' && _locality_a === 'NYC';
}
function getUSABoroughValue(record) {
// In NYC, the borough is used as the locality name on address lines
// (except in Queens, see below), so don't return a borough at all
// in NYC if there's a locality value to return
if (isInNYC(record) && getUSALocalValue(record)) {
// Ignore the borough, it's handled in getUSALocalValue
return undefined;
}
return getFirstProperty(['borough'])(record);
}
// NYC is special for addresses
// - The borough is used for the locality in addresses
// - Except in Queens, where ideally the neighbourhood is
// - Also, 'New York' is the proper locality name for Manhattan
function getNYCLocalValue(record) {
const _default = getFirstProperty(['locality', 'localadmin', 'county'])(record);
const _borough = getFirstProperty(['borough'])(record);
const _neighbourhood = getFirstProperty(['neighbourhood'])(record);
// We still want to return "neighborhood, borough, region_a" when a user searches for a neighborhood
// otherwise it looks incomplete, so skip to returning the borough in that case
// Otherwise, in Queens only, use the neighborhood for the city in address labels
if ('neighbourhood' !== record.layer &&
_borough &&
_borough.startsWith('Queens') &&
_neighbourhood
) {
return _neighbourhood;
} else if (_borough &&
_borough.startsWith('Manhattan')
) {
// return 'Manhattan, New York, for Manhattan neighbourhoods
if (record.layer === 'neighbourhood') {
return `${_borough}, ${_default}`;
// return only locality for Manhattan venues/addresses
} else{
return _default;
}
} else {
return _borough || _default;
}
}
function getUSALocalValue(record) {
const _default = getFirstProperty(['locality', 'localadmin', 'county'])(record);
// NYC is special for addresses
if (isInNYC(record)) {
return getNYCLocalValue(record);
}
return _default;
}
module.exports = {
'default': {
'valueFunctions': {
'local': getFirstProperty(['locality', 'localadmin']),
'regional': getUniqueRegionalValue,
'country': getFirstProperty(['dependency', 'country'])
}
},
'GBR': {
'valueFunctions': {
'local': getFirstProperty(['locality', 'localadmin']),
'regional': getFirstProperty(['macroregion']),
'country': getFirstProperty(['dependency', 'country'])
}
},
'USA': {
'valueFunctions': {
'borough': getUSABoroughValue,
'local': getUSALocalValue,
'regional': getRegionalValue,
'country': getUSADependencyOrCountryValue
}
},
'AUS': {
'valueFunctions': {
'local': getFirstProperty(['locality', 'localadmin']),
'regional': getRegionalValue,
'country': getFirstProperty(['dependency', 'country'])
}
},
'CAN': {
'valueFunctions': {
'local': getFirstProperty(['locality']), // no localadmins in CAN
'regional': getRegionalValue,
'country': getFirstProperty(['country'])
}
},
'KOR': {
'valueFunctions': {
'country': getFirstProperty(['country']),
'province': getFirstProperty(['region']),
'city': getFirstProperty(['county'])
},
'meta': {
'separator': ' ',
'builder': require('./builders/KOR')
}
},
'FRA': {
'valueFunctions': {
'local': getFirstProperty(['locality', 'localadmin']),
'country': getFRACountryValue()
}
},
'ITA': {
'valueFunctions': {
'local': getFirstProperty(['locality', 'localadmin']),
'regional': getRegionalValue,
'country': getFirstProperty(['country'])
}
},
'SGP': {
'valueFunctions': {
'local': getFirstProperty(['microhood', 'neighbourhood']),
'country': getFirstProperty(['country'])
}
},
'JPN': {
'languages': {
'JPN': {
'valueFunctions': {
'prefecture': getFirstProperty(['region']),
// outside of designated cities, this is the city name
'city': getFirstProperty(['county']),
// 20 important cities in Japan are known as 'designated cities'
// https://en.wikipedia.org/wiki/Cities_designated_by_government_ordinance_of_Japan
'designated-city': getFirstProperty(['locality']),
'machi': getFirstProperty(['borough']), // this is a middle layer between city and district for larger cities
'district': getFirstProperty(['neighbourhood']),
},
'meta': {
'builder': require('./builders/JPN-JPN'),
'separator': '' // no separation between most elements in Japanese
}
}
},
'valueFunctions': {
'district': getFirstProperty(['neighbourhood']),
'machi': getFirstProperty(['borough']), // this is a middle layer between city and district for larger cities
// 20 important cities in Japan are known as 'designated cities'
// https://en.wikipedia.org/wiki/Cities_designated_by_government_ordinance_of_Japan
'designated-city': getFirstProperty(['locality']),
// outside of designated cities, this is the city name
'city': getFirstProperty(['county']),
'prefecture': getFirstProperty(['region']),
'postalcode': getFirstProperty(['postalcode']),
'country': getFirstProperty(['country']),
},
'meta': {
'builder': require('./builders/JPN')
}
},
};