Skip to content

Commit 5709d1b

Browse files
committed
add attribute featureidkey
... to choroplethmapbox, scattergeo and choropleth traces. - Coerced only when `locationmode: 'geojson-id'`. To determine which key (or nested key) to use to identify eahc geojson feature. - Improve error messages in extractTraceFeature
1 parent 6397ab3 commit 5709d1b

File tree

14 files changed

+192
-24
lines changed

14 files changed

+192
-24
lines changed

src/lib/geo_location_utils.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var turfCentroid = require('@turf/centroid');
1616
var identity = require('./identity');
1717
var loggers = require('./loggers');
1818
var isPlainObject = require('./is_plain_object');
19+
var nestedProperty = require('./nested_property');
1920
var polygon = require('./polygon');
2021

2122
// make list of all country iso3 ids from at runtime
@@ -208,14 +209,16 @@ function extractTraceFeature(calcTrace) {
208209
}
209210

210211
function appendFeature(fIn) {
211-
var cdi = lookup[fIn.id];
212+
var id = nestedProperty(fIn, trace.featureidkey || 'id').get();
213+
var cdi = lookup[id];
212214

213215
if(cdi) {
214216
var geometry = fIn.geometry;
215217

216218
if(geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') {
217219
var fOut = {
218220
type: 'Feature',
221+
id: id,
219222
geometry: geometry,
220223
properties: {}
221224
};
@@ -230,15 +233,16 @@ function extractTraceFeature(calcTrace) {
230233
featuresOut.push(fOut);
231234
} else {
232235
loggers.log([
233-
'Location with id', cdi.loc, 'does not have a valid GeoJSON geometry,',
234-
'choroplethmapbox traces only support *Polygon* and *MultiPolygon* geometries.'
236+
'Location', cdi.loc, 'does not have a valid GeoJSON geometry.',
237+
'Traces with locationmode *geojson-id* only support',
238+
'*Polygon* and *MultiPolygon* geometries.'
235239
].join(' '));
236240
}
237241
}
238242

239243
// remove key from lookup, so that we can track (if any)
240244
// the locations that did not have a corresponding GeoJSON feature
241-
delete lookup[fIn.id];
245+
delete lookup[id];
242246
}
243247

244248
switch(geojsonIn.type) {
@@ -253,14 +257,19 @@ function extractTraceFeature(calcTrace) {
253257
break;
254258
default:
255259
loggers.warn([
256-
'Invalid GeoJSON type', (geojsonIn.type || 'none') + ',',
257-
'choroplethmapbox traces only support *FeatureCollection* and *Feature* types.'
260+
'Invalid GeoJSON type', (geojsonIn.type || 'none') + '.',
261+
'Traces with locationmode *geojson-id* only support',
262+
'*FeatureCollection* and *Feature* types.'
258263
].join(' '));
259264
return false;
260265
}
261266

262267
for(var loc in lookup) {
263-
loggers.log('Location with id ' + loc + ' does not have a matching feature');
268+
loggers.log([
269+
'Location *' + loc + '*',
270+
'does not have a matching feature with id-key',
271+
'*' + trace.featureidkey + '*.'
272+
].join(' '));
264273
}
265274

266275
return featuresOut;

src/traces/choropleth/attributes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ module.exports = extendFlat({
4646
// https://github.com/topojson/topojson-specification/blob/master/README.md
4747
].join(' ')
4848
}),
49+
featureidkey: scatterGeoAttrs.featureidkey,
50+
4951
text: extendFlat({}, scatterGeoAttrs.text, {
5052
description: 'Sets the text elements associated with each location.'
5153
}),

src/traces/choropleth/defaults.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,17 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
2828
traceOut._length = Math.min(locations.length, z.length);
2929

3030
var geojson = coerce('geojson');
31+
3132
var locationmodeDflt;
3233
if((typeof geojson === 'string' && geojson !== '') || Lib.isPlainObject(geojson)) {
3334
locationmodeDflt = 'geojson-id';
3435
}
35-
coerce('locationmode', locationmodeDflt);
36+
37+
var locationMode = coerce('locationmode', locationmodeDflt);
38+
39+
if(locationMode === 'geojson-id') {
40+
coerce('featureidkey');
41+
}
3642

3743
coerce('text');
3844
coerce('hovertext');

src/traces/choroplethmapbox/attributes.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ module.exports = extendFlat({
2828
// Maybe start with only one value (that we could name e.g. 'geojson-id'),
2929
// but eventually:
3030
// - we could also support for our own dist/topojson/*
31-
// - some people might want `geojson-properties-name` to map data arrays to
32-
// GeoJSON features
33-
// locationmode: choroplethAttrs.locationmode,
31+
// .. and locationmode: choroplethAttrs.locationmode,
3432

3533
z: {
3634
valType: 'data_array',
@@ -52,6 +50,13 @@ module.exports = extendFlat({
5250
'with geometries of type *Polygon* and *MultiPolygon*.'
5351
].join(' ')
5452
},
53+
featureidkey: extendFlat({}, choroplethAttrs.featureidkey, {
54+
description: [
55+
'Sets the key in GeoJSON features which is used as id to match the items',
56+
'included in the `locations` array.',
57+
'Support nested property, for example *properties.name*.'
58+
].join(' ')
59+
}),
5560

5661
// TODO agree on name / behaviour
5762
//

src/traces/choroplethmapbox/defaults.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
2929
return;
3030
}
3131

32+
coerce('featureidkey');
33+
3234
traceOut._length = Math.min(locations.length, z.length);
3335

3436
coerce('below');

src/traces/scattergeo/attributes.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ module.exports = overrideAll({
7070
// https://github.com/topojson/topojson-specification/blob/master/README.md
7171
].join(' ')
7272
},
73+
featureidkey: {
74+
valType: 'string',
75+
role: 'info',
76+
editType: 'calc',
77+
dflt: 'id',
78+
description: [
79+
'Sets the key in GeoJSON features which is used as id to match the items',
80+
'included in the `locations` array.',
81+
'Only has an effect when `geojson` is set.',
82+
'Support nested property, for example *properties.name*.'
83+
].join(' ')
84+
},
7385

7486
mode: extendFlat({}, scatterAttrs.mode, {dflt: 'markers'}),
7587

src/traces/scattergeo/defaults.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3232
if((typeof geojson === 'string' && geojson !== '') || Lib.isPlainObject(geojson)) {
3333
locationmodeDflt = 'geojson-id';
3434
}
35-
coerce('locationmode', locationmodeDflt);
35+
36+
var locationMode = coerce('locationmode', locationmodeDflt);
37+
38+
if(locationMode === 'geojson-id') {
39+
coerce('featureidkey');
40+
}
41+
3642
len = locations.length;
3743
} else {
3844
var lon = coerce('lon') || [];
19.8 KB
Loading
Loading
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{
2+
"data": [
3+
{
4+
"type": "scattergeo",
5+
"locations": ["AL"],
6+
"featureidkey": "properties.name",
7+
"geojson": {
8+
"type": "Feature",
9+
"properties": {
10+
"name": "AL"
11+
},
12+
"geometry": {
13+
"type": "Polygon",
14+
"coordinates": [[
15+
[-87.359296, 35.00118], [-85.606675, 34.984749], [-85.431413, 34.124869], [-85.184951, 32.859696],
16+
[-85.069935, 32.580372], [-84.960397, 32.421541], [-85.004212, 32.322956], [-84.889196, 32.262709],
17+
[-85.058981, 32.13674], [-85.053504, 32.01077], [-85.141136, 31.840985], [-85.042551, 31.539753],
18+
[-85.113751, 31.27686], [-85.004212, 31.003013], [-85.497137, 30.997536], [-87.600282, 30.997536],
19+
[-87.633143, 30.86609], [-87.408589, 30.674397], [-87.446927, 30.510088], [-87.37025, 30.427934],
20+
[-87.518128, 30.280057], [-87.655051, 30.247195], [-87.90699, 30.411504], [-87.934375, 30.657966],
21+
[-88.011052, 30.685351], [-88.10416, 30.499135], [-88.137022, 30.318396], [-88.394438, 30.367688],
22+
[-88.471115, 31.895754], [-88.241084, 33.796253], [-88.098683, 34.891641], [-88.202745, 34.995703],
23+
[-87.359296, 35.00118]
24+
]]
25+
}
26+
}
27+
},
28+
{
29+
"type": "choropleth",
30+
"name": "choropleth + RAW",
31+
"locations": ["AL"],
32+
"featureidkey": "properties.id",
33+
"z": [10],
34+
"showscale": false,
35+
"geojson": {
36+
"type": "Feature",
37+
"properties": {
38+
"id": "AL"
39+
},
40+
"geometry": {
41+
"type": "Polygon",
42+
"coordinates": [[
43+
[-87.359296, 35.00118], [-85.606675, 34.984749], [-85.431413, 34.124869], [-85.184951, 32.859696],
44+
[-85.069935, 32.580372], [-84.960397, 32.421541], [-85.004212, 32.322956], [-84.889196, 32.262709],
45+
[-85.058981, 32.13674], [-85.053504, 32.01077], [-85.141136, 31.840985], [-85.042551, 31.539753],
46+
[-85.113751, 31.27686], [-85.004212, 31.003013], [-85.497137, 30.997536], [-87.600282, 30.997536],
47+
[-87.633143, 30.86609], [-87.408589, 30.674397], [-87.446927, 30.510088], [-87.37025, 30.427934],
48+
[-87.518128, 30.280057], [-87.655051, 30.247195], [-87.90699, 30.411504], [-87.934375, 30.657966],
49+
[-88.011052, 30.685351], [-88.10416, 30.499135], [-88.137022, 30.318396], [-88.394438, 30.367688],
50+
[-88.471115, 31.895754], [-88.241084, 33.796253], [-88.098683, 34.891641], [-88.202745, 34.995703],
51+
[-87.359296, 35.00118]
52+
]]
53+
}
54+
}
55+
}
56+
],
57+
"layout": {
58+
"geo": {
59+
"center": { "lon": -86, "lat": 33 },
60+
"projection": {"scale": 30}
61+
},
62+
"width": 600,
63+
"height": 400,
64+
"showlegend": false,
65+
"title": {
66+
"text": "Geo traces with set <b>featureidkey</b>",
67+
"x": 0.1,
68+
"xref": "container",
69+
"xanchor": "left"
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)