-
Notifications
You must be signed in to change notification settings - Fork 0
/
iowa-dash.json
181 lines (181 loc) · 64.3 KB
/
iowa-dash.json
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
[
{
"_id": "0116b7f0-7a18-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "CountyToCategoryGallons-Sankey",
"visState": "{\"title\":\"CountyToCategoryGallons-Sankey\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega/v3.0.json\\n data: [\\n {\\n // query ES based on the currently selected time range and filter string\\n name: rawData\\n url: {\\n %context%: true\\n %timefield%: DateofSale\\n index: iowa-liquor2\\n body: {\\n size: 0\\n aggs: {\\n table: {\\n composite: {\\n size: 10000\\n sources: [\\n {\\n stk1: {\\n terms: {field: \\\"County.keyword\\\", order: \\\"desc\\\"}\\n }\\n }\\n {\\n stk2: {\\n terms: {field: \\\"CategoryName.keyword\\\", order: \\\"desc\\\"}\\n }\\n }\\n ]\\n }\\n aggs: {\\n sum: {\\n sum: {field:\\n \\\"VolumeSoldGallons\\\"}\\n }\\n }\\n }\\n }\\n }\\n }\\n // From the result, take just the data we are interested in\\n format: {property: \\\"aggregations.table.buckets\\\"}\\n // Convert key.stk1 -> stk1 for simpler access below\\n transform: [\\n {\\n type: formula\\n expr: datum.key.stk1\\n as: stk1\\n }\\n {\\n type: formula\\n expr: datum.key.stk2\\n as: stk2\\n }\\n {\\n type: formula\\n expr: datum.sum.value\\n as: size\\n }\\n ]\\n }\\n {\\n name: nodes\\n source: rawData\\n transform: [\\n // when a country is selected, filter out unrelated data\\n {\\n type: filter\\n expr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\n }\\n // Set new key for later lookups - identifies each node\\n {\\n type: formula\\n expr: datum.stk1+datum.stk2\\n as: key\\n }\\n // instead of each table row, create two new rows,\\n // one for the source (stack=stk1) and one for destination node (stack=stk2).\\n // The country code stored in stk1 and stk2 fields is placed into grpId field.\\n {\\n type: fold\\n fields: [\\\"stk1\\\", \\\"stk2\\\"]\\n as: [\\\"stack\\\", \\\"grpId\\\"]\\n }\\n // Create a sortkey, different for stk1 and stk2 stacks.\\n {\\n type: formula\\n expr: datum.stack == 'stk1' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\n as: sortField\\n }\\n // Calculate y0 and y1 positions for stacking nodes one on top of the other,\\n // independently for each stack, and ensuring they are in the proper order,\\n // alphabetical from the top (reversed on the y axis)\\n {\\n type: stack\\n groupby: [\\\"stack\\\"]\\n sort: {field: \\\"sortField\\\", order: \\\"descending\\\"}\\n field: size\\n }\\n // calculate vertical center point for each node, used to draw edges\\n {\\n type: formula\\n expr: (datum.y0+datum.y1)/2\\n as: yc\\n }\\n ]\\n }\\n {\\n name: groups\\n source: nodes\\n transform: [\\n // combine all nodes into country groups, summing up the doc counts\\n {\\n type: aggregate\\n groupby: [\\\"stack\\\", \\\"grpId\\\"]\\n fields: [\\\"size\\\"]\\n ops: [\\\"sum\\\"]\\n as: [\\\"total\\\"]\\n }\\n // re-calculate the stacking y0,y1 values\\n {\\n type: stack\\n groupby: [\\\"stack\\\"]\\n sort: {field: \\\"grpId\\\", order: \\\"descending\\\"}\\n field: total\\n }\\n // project y0 and y1 values to screen coordinates\\n // doing it once here instead of doing it several times in marks\\n {\\n type: formula\\n expr: scale('y', datum.y0)\\n as: scaledY0\\n }\\n {\\n type: formula\\n expr: scale('y', datum.y1)\\n as: scaledY1\\n }\\n // boolean flag if the label should be on the right of the stack\\n {\\n type: formula\\n expr: datum.stack == 'stk1'\\n as: rightLabel\\n }\\n // Calculate traffic percentage for this country using \\\"y\\\" scale\\n // domain upper bound, which represents the total traffic\\n {\\n type: formula\\n expr: datum.total/domain('y')[1]\\n as: percentage\\n }\\n ]\\n }\\n {\\n // This is a temp lookup table with all the 'stk2' stack nodes\\n name: destinationNodes\\n source: nodes\\n transform: [\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk2'\\\"}\\n ]\\n }\\n {\\n name: edges\\n source: nodes\\n transform: [\\n // we only want nodes from the left stack\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk1'\\\"}\\n // find corresponding node from the right stack, keep it as \\\"target\\\"\\n {\\n type: lookup\\n from: destinationNodes\\n key: key\\n fields: [\\\"key\\\"]\\n as: [\\\"target\\\"]\\n }\\n // calculate SVG link path between stk1 and stk2 stacks for the node pair\\n {\\n type: linkpath\\n orient: horizontal\\n shape: diagonal\\n sourceY: {expr: \\\"scale('y', datum.yc)\\\"}\\n sourceX: {expr: \\\"scale('x', 'stk1') + bandwidth('x')\\\"}\\n targetY: {expr: \\\"scale('y', datum.target.yc)\\\"}\\n targetX: {expr: \\\"scale('x', 'stk2')\\\"}\\n }\\n // A little trick to calculate the thickness of the line.\\n // The value needs to be the same as the hight of the node, but scaling\\n // size to screen's height gives inversed value because screen's Y\\n // coordinate goes from the top to the bottom, whereas the graph's Y=0\\n // is at the bottom. So subtracting scaled doc count from screen height\\n // (which is the \\\"lower\\\" bound of the \\\"y\\\" scale) gives us the right value\\n {\\n type: formula\\n expr: range('y')[0]-scale('y', datum.size)\\n as: strokeWidth\\n }\\n // Tooltip needs individual link's percentage of all traffic\\n {\\n type: formula\\n expr: datum.size/domain('y')[1]\\n as: percentage\\n }\\n ]\\n }\\n ]\\n scales: [\\n {\\n // calculates horizontal stack positioning\\n name: x\\n type: band\\n range: width\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\n paddingOuter: 0.05\\n paddingInner: 0.95\\n }\\n {\\n // this scale goes up as high as the highest y1 value of all nodes\\n name: y\\n type: linear\\n range: height\\n domain: {data: \\\"nodes\\\", field: \\\"y1\\\"}\\n }\\n {\\n // use rawData to ensure the colors stay the same when clicking.\\n name: color\\n type: ordinal\\n range: category\\n domain: {data: \\\"rawData\\\", field: \\\"stk1\\\"}\\n }\\n {\\n // this scale is used to map internal ids (stk1, stk2) to stack names\\n name: stackNames\\n type: ordinal\\n range: [\\\"Source\\\", \\\"Destination\\\"]\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\n }\\n ]\\n axes: [\\n {\\n // x axis should use custom label formatting to print proper stack names\\n orient: bottom\\n scale: x\\n encode: {\\n labels: {\\n update: {\\n text: {scale: \\\"stackNames\\\", field: \\\"value\\\"}\\n }\\n }\\n }\\n }\\n {orient: \\\"left\\\", scale: \\\"y\\\"}\\n ]\\n marks: [\\n {\\n // draw the connecting line between stacks\\n type: path\\n name: edgeMark\\n from: {data: \\\"edges\\\"}\\n // this prevents some autosizing issues with large strokeWidth for paths\\n clip: true\\n encode: {\\n update: {\\n // By default use color of the left node, except when showing traffic\\n // from just one country, in which case use destination color.\\n stroke: [\\n {\\n test: groupSelector && groupSelector.stack=='stk1'\\n scale: color\\n field: stk2\\n }\\n {scale: \\\"color\\\", field: \\\"stk1\\\"}\\n ]\\n strokeWidth: {field: \\\"strokeWidth\\\"}\\n path: {field: \\\"path\\\"}\\n // when showing all traffic, and hovering over a country,\\n // highlight the traffic from that country.\\n strokeOpacity: {\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\n }\\n // Ensure that the hover-selected edges show on top\\n zindex: {\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\n }\\n // format tooltip string\\n tooltip: {\\n signal: datum.stk1 + ' → ' + datum.stk2 + ' ' + format(datum.size, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\n }\\n }\\n // Simple mouseover highlighting of a single line\\n hover: {\\n strokeOpacity: {value: 1}\\n }\\n }\\n }\\n {\\n // draw stack groups (countries)\\n type: rect\\n name: groupMark\\n from: {data: \\\"groups\\\"}\\n encode: {\\n enter: {\\n fill: {scale: \\\"color\\\", field: \\\"grpId\\\"}\\n width: {scale: \\\"x\\\", band: 1}\\n }\\n update: {\\n x: {scale: \\\"x\\\", field: \\\"stack\\\"}\\n y: {field: \\\"scaledY0\\\"}\\n y2: {field: \\\"scaledY1\\\"}\\n fillOpacity: {value: 0.6}\\n tooltip: {\\n signal: datum.grpId + ' ' + format(datum.total, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\n }\\n }\\n hover: {\\n fillOpacity: {value: 1}\\n }\\n }\\n }\\n {\\n // draw country code labels on the inner side of the stack\\n type: text\\n from: {data: \\\"groups\\\"}\\n // don't process events for the labels - otherwise line mouseover is unclean\\n interactive: false\\n encode: {\\n update: {\\n // depending on which stack it is, position x with some padding\\n x: {\\n signal: scale('x', datum.stack) + (datum.rightLabel ? bandwidth('x') + 8 : -8)\\n }\\n // middle of the group\\n yc: {signal: \\\"(datum.scaledY0 + datum.scaledY1)/2\\\"}\\n align: {signal: \\\"datum.rightLabel ? 'left' : 'right'\\\"}\\n baseline: {value: \\\"middle\\\"}\\n fontWeight: {value: \\\"bold\\\"}\\n // only show text label if the group's height is large enough\\n text: {\\n signal: abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : ''\\n }\\n }\\n }\\n }\\n {\\n // Create a \\\"show all\\\" button. Shown only when a country is selected.\\n type: group\\n data: [\\n // We need to make the button show only when groupSelector signal is true.\\n // Each mark is drawn as many times as there are elements in the backing data.\\n // Which means that if values list is empty, it will not be drawn.\\n // Here I create a data source with one empty object, and filter that list\\n // based on the signal value. This can only be done in a group.\\n {\\n name: dataForShowAll\\n values: [\\n {}\\n ]\\n transform: [\\n {type: \\\"filter\\\", expr: \\\"groupSelector\\\"}\\n ]\\n }\\n ]\\n // Set button size and positioning\\n encode: {\\n enter: {\\n xc: {signal: \\\"width/2\\\"}\\n y: {value: 30}\\n width: {value: 80}\\n height: {value: 30}\\n }\\n }\\n marks: [\\n {\\n // This group is shown as a button with rounded corners.\\n type: group\\n // mark name allows signal capturing\\n name: groupReset\\n // Only shows button if dataForShowAll has values.\\n from: {data: \\\"dataForShowAll\\\"}\\n encode: {\\n enter: {\\n cornerRadius: {value: 6}\\n fill: {value: \\\"#f5f5f5\\\"}\\n stroke: {value: \\\"#c1c1c1\\\"}\\n strokeWidth: {value: 2}\\n // use parent group's size\\n height: {\\n field: {group: \\\"height\\\"}\\n }\\n width: {\\n field: {group: \\\"width\\\"}\\n }\\n }\\n update: {\\n // groups are transparent by default\\n opacity: {value: 1}\\n }\\n hover: {\\n opacity: {value: 0.7}\\n }\\n }\\n marks: [\\n {\\n type: text\\n // if true, it will prevent clicking on the button when over text.\\n interactive: false\\n encode: {\\n enter: {\\n // center text in the paren group\\n xc: {\\n field: {group: \\\"width\\\"}\\n mult: 0.5\\n }\\n yc: {\\n field: {group: \\\"height\\\"}\\n mult: 0.5\\n offset: 2\\n }\\n align: {value: \\\"center\\\"}\\n baseline: {value: \\\"middle\\\"}\\n fontWeight: {value: \\\"bold\\\"}\\n text: {value: \\\"Show All\\\"}\\n }\\n }\\n }\\n ]\\n }\\n ]\\n }\\n ]\\n signals: [\\n {\\n // used to highlight traffic to/from the same country\\n name: groupHover\\n value: {}\\n on: [\\n {\\n events: @groupMark:mouseover\\n update: \\\"{stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\n }\\n {events: \\\"mouseout\\\", update: \\\"{}\\\"}\\n ]\\n }\\n // used to filter only the data related to the selected country\\n {\\n name: groupSelector\\n value: false\\n on: [\\n {\\n // Clicking groupMark sets this signal to the filter values\\n events: @groupMark:click!\\n update: \\\"{stack:datum.stack, stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\n }\\n {\\n // Clicking \\\"show all\\\" button, or double-clicking anywhere resets it\\n events: [\\n {type: \\\"click\\\", markname: \\\"groupReset\\\"}\\n {type: \\\"dblclick\\\"}\\n ]\\n update: \\\"false\\\"\\n }\\n ]\\n }\\n ]\\n}\"},\"aggs\":[]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "e06b02c0-78c5-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "CountyToCategory-Sankey",
"visState": "{\"title\":\"CountyToCategory-Sankey\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega/v3.0.json\\n data: [\\n {\\n // query ES based on the currently selected time range and filter string\\n name: rawData\\n url: {\\n %context%: true\\n %timefield%: DateofSale\\n index: iowa-liquor2\\n body: {\\n size: 0\\n aggs: {\\n table: {\\n composite: {\\n size: 10000\\n sources: [\\n {\\n stk1: {\\n terms: {field: \\\"County.keyword\\\",\\\"order\\\": \\\"desc\\\"}\\n }\\n }\\n {\\n stk2: {\\n terms: {field: \\\"CategoryName.keyword\\\",\\\"order\\\": \\\"desc\\\"}\\n }\\n }\\n ]\\n }\\n }\\n }\\n }\\n }\\n // From the result, take just the data we are interested in\\n format: {property: \\\"aggregations.table.buckets\\\"}\\n // Convert key.stk1 -> stk1 for simpler access below\\n transform: [\\n {type: \\\"formula\\\", expr: \\\"datum.key.stk1\\\", as: \\\"stk1\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.key.stk2\\\", as: \\\"stk2\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.doc_count\\\", as: \\\"size\\\"}\\n ]\\n }\\n {\\n name: nodes\\n source: rawData\\n transform: [\\n // when a country is selected, filter out unrelated data\\n {\\n type: filter\\n expr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\n }\\n // Set new key for later lookups - identifies each node\\n {type: \\\"formula\\\", expr: \\\"datum.stk1+datum.stk2\\\", as: \\\"key\\\"}\\n // instead of each table row, create two new rows,\\n // one for the source (stack=stk1) and one for destination node (stack=stk2).\\n // The country code stored in stk1 and stk2 fields is placed into grpId field.\\n {\\n type: fold\\n fields: [\\\"stk1\\\", \\\"stk2\\\"]\\n as: [\\\"stack\\\", \\\"grpId\\\"]\\n }\\n // Create a sortkey, different for stk1 and stk2 stacks.\\n {\\n type: formula\\n expr: datum.stack == 'stk1' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\n as: sortField\\n }\\n // Calculate y0 and y1 positions for stacking nodes one on top of the other,\\n // independently for each stack, and ensuring they are in the proper order,\\n // alphabetical from the top (reversed on the y axis)\\n {\\n type: stack\\n groupby: [\\\"stack\\\"]\\n sort: {field: \\\"sortField\\\", order: \\\"descending\\\"}\\n field: size\\n }\\n // calculate vertical center point for each node, used to draw edges\\n {type: \\\"formula\\\", expr: \\\"(datum.y0+datum.y1)/2\\\", as: \\\"yc\\\"}\\n ]\\n }\\n {\\n name: groups\\n source: nodes\\n transform: [\\n // combine all nodes into country groups, summing up the doc counts\\n {\\n type: aggregate\\n groupby: [\\\"stack\\\", \\\"grpId\\\"]\\n fields: [\\\"size\\\"]\\n ops: [\\\"sum\\\"]\\n as: [\\\"total\\\"]\\n }\\n // re-calculate the stacking y0,y1 values\\n {\\n type: stack\\n groupby: [\\\"stack\\\"]\\n sort: {field: \\\"grpId\\\", order: \\\"descending\\\"}\\n field: total\\n }\\n // project y0 and y1 values to screen coordinates\\n // doing it once here instead of doing it several times in marks\\n {type: \\\"formula\\\", expr: \\\"scale('y', datum.y0)\\\", as: \\\"scaledY0\\\"}\\n {type: \\\"formula\\\", expr: \\\"scale('y', datum.y1)\\\", as: \\\"scaledY1\\\"}\\n // boolean flag if the label should be on the right of the stack\\n {type: \\\"formula\\\", expr: \\\"datum.stack == 'stk1'\\\", as: \\\"rightLabel\\\"}\\n // Calculate traffic percentage for this country using \\\"y\\\" scale\\n // domain upper bound, which represents the total traffic\\n {\\n type: formula\\n expr: datum.total/domain('y')[1]\\n as: percentage\\n }\\n ]\\n }\\n {\\n // This is a temp lookup table with all the 'stk2' stack nodes\\n name: destinationNodes\\n source: nodes\\n transform: [\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk2'\\\"}\\n ]\\n }\\n {\\n name: edges\\n source: nodes\\n transform: [\\n // we only want nodes from the left stack\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk1'\\\"}\\n // find corresponding node from the right stack, keep it as \\\"target\\\"\\n {\\n type: lookup\\n from: destinationNodes\\n key: key\\n fields: [\\\"key\\\"]\\n as: [\\\"target\\\"]\\n }\\n // calculate SVG link path between stk1 and stk2 stacks for the node pair\\n {\\n type: linkpath\\n orient: horizontal\\n shape: diagonal\\n sourceY: {expr: \\\"scale('y', datum.yc)\\\"}\\n sourceX: {expr: \\\"scale('x', 'stk1') + bandwidth('x')\\\"}\\n targetY: {expr: \\\"scale('y', datum.target.yc)\\\"}\\n targetX: {expr: \\\"scale('x', 'stk2')\\\"}\\n }\\n // A little trick to calculate the thickness of the line.\\n // The value needs to be the same as the hight of the node, but scaling\\n // size to screen's height gives inversed value because screen's Y\\n // coordinate goes from the top to the bottom, whereas the graph's Y=0\\n // is at the bottom. So subtracting scaled doc count from screen height\\n // (which is the \\\"lower\\\" bound of the \\\"y\\\" scale) gives us the right value\\n {\\n type: formula\\n expr: range('y')[0]-scale('y', datum.size)\\n as: strokeWidth\\n }\\n // Tooltip needs individual link's percentage of all traffic\\n {\\n type: formula\\n expr: datum.size/domain('y')[1]\\n as: percentage\\n }\\n ]\\n }\\n ]\\n scales: [\\n {\\n // calculates horizontal stack positioning\\n name: x\\n type: band\\n range: width\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\n paddingOuter: 0.05\\n paddingInner: 0.95\\n }\\n {\\n // this scale goes up as high as the highest y1 value of all nodes\\n name: y\\n type: linear\\n range: height\\n domain: {data: \\\"nodes\\\", field: \\\"y1\\\"}\\n }\\n {\\n // use rawData to ensure the colors stay the same when clicking.\\n name: color\\n type: ordinal\\n range: category\\n domain: {data: \\\"rawData\\\", field: \\\"stk1\\\"}\\n }\\n {\\n // this scale is used to map internal ids (stk1, stk2) to stack names\\n name: stackNames\\n type: ordinal\\n range: [\\\"Source\\\", \\\"Destination\\\"]\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\n }\\n ]\\n axes: [\\n {\\n // x axis should use custom label formatting to print proper stack names\\n orient: bottom\\n scale: x\\n encode: {\\n labels: {\\n update: {\\n text: {scale: \\\"stackNames\\\", field: \\\"value\\\"}\\n }\\n }\\n }\\n }\\n {orient: \\\"left\\\", scale: \\\"y\\\"}\\n ]\\n marks: [\\n {\\n // draw the connecting line between stacks\\n type: path\\n name: edgeMark\\n from: {data: \\\"edges\\\"}\\n // this prevents some autosizing issues with large strokeWidth for paths\\n clip: true\\n encode: {\\n update: {\\n // By default use color of the left node, except when showing traffic\\n // from just one country, in which case use destination color.\\n stroke: [\\n {\\n test: groupSelector && groupSelector.stack=='stk1'\\n scale: color\\n field: stk2\\n }\\n {scale: \\\"color\\\", field: \\\"stk1\\\"}\\n ]\\n strokeWidth: {field: \\\"strokeWidth\\\"}\\n path: {field: \\\"path\\\"}\\n // when showing all traffic, and hovering over a country,\\n // highlight the traffic from that country.\\n strokeOpacity: {\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\n }\\n // Ensure that the hover-selected edges show on top\\n zindex: {\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\n }\\n // format tooltip string\\n tooltip: {\\n signal: datum.stk1 + ' → ' + datum.stk2 + ' ' + format(datum.size, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\n }\\n }\\n // Simple mouseover highlighting of a single line\\n hover: {\\n strokeOpacity: {value: 1}\\n }\\n }\\n }\\n {\\n // draw stack groups (countries)\\n type: rect\\n name: groupMark\\n from: {data: \\\"groups\\\"}\\n encode: {\\n enter: {\\n fill: {scale: \\\"color\\\", field: \\\"grpId\\\"}\\n width: {scale: \\\"x\\\", band: 1}\\n }\\n update: {\\n x: {scale: \\\"x\\\", field: \\\"stack\\\"}\\n y: {field: \\\"scaledY0\\\"}\\n y2: {field: \\\"scaledY1\\\"}\\n fillOpacity: {value: 0.6}\\n tooltip: {\\n signal: datum.grpId + ' ' + format(datum.total, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\n }\\n }\\n hover: {\\n fillOpacity: {value: 1}\\n }\\n }\\n }\\n {\\n // draw country code labels on the inner side of the stack\\n type: text\\n from: {data: \\\"groups\\\"}\\n // don't process events for the labels - otherwise line mouseover is unclean\\n interactive: false\\n encode: {\\n update: {\\n // depending on which stack it is, position x with some padding\\n x: {\\n signal: scale('x', datum.stack) + (datum.rightLabel ? bandwidth('x') + 8 : -8)\\n }\\n // middle of the group\\n yc: {signal: \\\"(datum.scaledY0 + datum.scaledY1)/2\\\"}\\n align: {signal: \\\"datum.rightLabel ? 'left' : 'right'\\\"}\\n baseline: {value: \\\"middle\\\"}\\n fontWeight: {value: \\\"bold\\\"}\\n // only show text label if the group's height is large enough\\n text: {signal: \\\"abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : ''\\\"}\\n }\\n }\\n }\\n {\\n // Create a \\\"show all\\\" button. Shown only when a country is selected.\\n type: group\\n data: [\\n // We need to make the button show only when groupSelector signal is true.\\n // Each mark is drawn as many times as there are elements in the backing data.\\n // Which means that if values list is empty, it will not be drawn.\\n // Here I create a data source with one empty object, and filter that list\\n // based on the signal value. This can only be done in a group.\\n {\\n name: dataForShowAll\\n values: [{}]\\n transform: [{type: \\\"filter\\\", expr: \\\"groupSelector\\\"}]\\n }\\n ]\\n // Set button size and positioning\\n encode: {\\n enter: {\\n xc: {signal: \\\"width/2\\\"}\\n y: {value: 30}\\n width: {value: 80}\\n height: {value: 30}\\n }\\n }\\n marks: [\\n {\\n // This group is shown as a button with rounded corners.\\n type: group\\n // mark name allows signal capturing\\n name: groupReset\\n // Only shows button if dataForShowAll has values.\\n from: {data: \\\"dataForShowAll\\\"}\\n encode: {\\n enter: {\\n cornerRadius: {value: 6}\\n fill: {value: \\\"#f5f5f5\\\"}\\n stroke: {value: \\\"#c1c1c1\\\"}\\n strokeWidth: {value: 2}\\n // use parent group's size\\n height: {\\n field: {group: \\\"height\\\"}\\n }\\n width: {\\n field: {group: \\\"width\\\"}\\n }\\n }\\n update: {\\n // groups are transparent by default\\n opacity: {value: 1}\\n }\\n hover: {\\n opacity: {value: 0.7}\\n }\\n }\\n marks: [\\n {\\n type: text\\n // if true, it will prevent clicking on the button when over text.\\n interactive: false\\n encode: {\\n enter: {\\n // center text in the paren group\\n xc: {\\n field: {group: \\\"width\\\"}\\n mult: 0.5\\n }\\n yc: {\\n field: {group: \\\"height\\\"}\\n mult: 0.5\\n offset: 2\\n }\\n align: {value: \\\"center\\\"}\\n baseline: {value: \\\"middle\\\"}\\n fontWeight: {value: \\\"bold\\\"}\\n text: {value: \\\"Show All\\\"}\\n }\\n }\\n }\\n ]\\n }\\n ]\\n }\\n ]\\n signals: [\\n {\\n // used to highlight traffic to/from the same country\\n name: groupHover\\n value: {}\\n on: [\\n {\\n events: @groupMark:mouseover\\n update: \\\"{stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\n }\\n {events: \\\"mouseout\\\", update: \\\"{}\\\"}\\n ]\\n }\\n // used to filter only the data related to the selected country\\n {\\n name: groupSelector\\n value: false\\n on: [\\n {\\n // Clicking groupMark sets this signal to the filter values\\n events: @groupMark:click!\\n update: \\\"{stack:datum.stack, stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\n }\\n {\\n // Clicking \\\"show all\\\" button, or double-clicking anywhere resets it\\n events: [\\n {type: \\\"click\\\", markname: \\\"groupReset\\\"}\\n {type: \\\"dblclick\\\"}\\n ]\\n update: \\\"false\\\"\\n }\\n ]\\n }\\n ]\\n}\\n\"},\"aggs\":[]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "f4ba6460-7a18-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "Statistics",
"visState": "{\"title\":\"Statistics\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n \\\"$schema\\\": \\\"https://vega.github.io/schema/vega/v3.json\\\",\\n \\\"padding\\\": 5,\\n\\n \\\"config\\\": {\\n \\\"axisBand\\\": {\\n \\\"bandPosition\\\": 1,\\n \\\"tickExtra\\\": true,\\n \\\"tickOffset\\\": 0\\n }\\n },\\n\\n \\\"signals\\\": [\\n {\\n \\\"name\\\": \\\"errorMeasure\\\", \\\"value\\\": \\\"Standard Deviation\\\",\\n \\\"bind\\\": {\\\"input\\\": \\\"select\\\", \\\"options\\\": [\\n \\\"5-95% Percentiles\\\",\\n \\\"Standard Deviation\\\"\\n ]}\\n },\\n {\\n \\\"name\\\": \\\"lookup\\\",\\n \\\"value\\\": {\\n \\\"5-95% Percentiles\\\": \\\"ci\\\",\\n \\\"Standard Deviation\\\": \\\"stdev\\\"\\n }\\n },\\n {\\n \\\"name\\\": \\\"measure\\\",\\n \\\"update\\\": \\\"lookup[errorMeasure]\\\"\\n }\\n ],\\n\\ndata: [\\n {\\n // query ES based on the currently selected time range and filter string\\n name: summary\\n url: {\\n %context%: true\\n %timefield%: DateofSale\\n index: iowa-liquor2\\n body: {\\n size: 0\\n \\\"aggs\\\": {\\n \\\"table\\\": {\\n \\\"terms\\\": {\\n \\\"field\\\": \\\"CategoryName.keyword\\\",\\n \\\"size\\\": 10,\\n \\\"order\\\": {\\n \\\"_key\\\": \\\"desc\\\"\\n }\\n },\\n \\\"aggs\\\": {\\n \\\"stats\\\": {\\n \\\"extended_stats\\\": {\\n \\\"field\\\": \\\"SaleDollars\\\"\\n }\\n },\\n \\\"percentiles\\\": {\\n \\\"percentiles\\\": {\\n \\\"field\\\": \\\"SaleDollars\\\",\\n \\\"percents\\\": [\\n 5,\\n 95\\n ]\\n }\\n }\\n }\\n }\\n }\\n }\\n }\\n // From the result, take just the data we are interested in\\n format: {property: \\\"aggregations.table.buckets\\\"}\\n // Convert key.stk1 -> stk1 for simpler access below\\n transform: [\\n {type: \\\"formula\\\", expr: \\\"datum.key\\\", as: \\\"key\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.stats.avg\\\", as: \\\"mean\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.stats.min\\\", as: \\\"min\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.stats.max\\\", as: \\\"max\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.stats.std_deviation\\\", as: \\\"stdev\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.stats.std_deviation_bounds.lower\\\", as: \\\"stdev0\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.stats.std_deviation_bounds.upper\\\", as: \\\"stdev1\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.percentiles.values['5.0']\\\", as: \\\"ci0\\\"}\\n {type: \\\"formula\\\", expr: \\\"datum.percentiles.values['95.0']\\\", as: \\\"ci1\\\"}\\n ]\\n }\\n]\\n\\n \\\"scales\\\": [\\n {\\n \\\"name\\\": \\\"yscale\\\",\\n \\\"type\\\": \\\"band\\\",\\n \\\"range\\\": \\\"height\\\",\\n \\\"domain\\\": {\\n \\\"data\\\": \\\"summary\\\",\\n \\\"field\\\": \\\"key\\\",\\n \\\"sort\\\": {\\\"op\\\": \\\"max\\\", \\\"field\\\": \\\"mean\\\", \\\"order\\\": \\\"descending\\\"}\\n }\\n },\\n {\\n \\\"name\\\": \\\"xscale\\\",\\n \\\"type\\\": \\\"linear\\\",\\n \\\"range\\\": \\\"width\\\", \\\"round\\\": true,\\n \\\"domain\\\": {\\\"data\\\": \\\"summary\\\", \\\"fields\\\": [\\\"min\\\", \\\"max\\\"]},\\n \\\"zero\\\": false, \\\"nice\\\": true\\n }\\n ],\\n\\n \\\"axes\\\": [\\n {\\\"orient\\\": \\\"bottom\\\", \\\"scale\\\": \\\"xscale\\\", \\\"zindex\\\": 1, \\\"title\\\": \\\"Barley Yield\\\"},\\n {\\\"orient\\\": \\\"left\\\", \\\"scale\\\": \\\"yscale\\\", \\\"tickCount\\\": 5, \\\"zindex\\\": 1}\\n ],\\n\\n \\\"marks\\\": [\\n {\\n \\\"type\\\": \\\"rect\\\",\\n \\\"from\\\": {\\\"data\\\": \\\"summary\\\"},\\n \\\"encode\\\": {\\n \\\"enter\\\": {\\n \\\"fill\\\": {\\\"value\\\": \\\"black\\\"},\\n \\\"height\\\": {\\\"value\\\": 1}\\n },\\n \\\"update\\\": {\\n \\\"y\\\": {\\\"scale\\\": \\\"yscale\\\", \\\"field\\\": \\\"key\\\", \\\"band\\\": 0.5},\\n \\\"x\\\": {\\\"scale\\\": \\\"xscale\\\", \\\"signal\\\": \\\"datum[measure+'0']\\\"},\\n \\\"x2\\\": {\\\"scale\\\": \\\"xscale\\\", \\\"signal\\\": \\\"datum[measure+'1']\\\"}\\n }\\n }\\n },\\n {\\n \\\"type\\\": \\\"symbol\\\",\\n \\\"from\\\": {\\\"data\\\": \\\"summary\\\"},\\n \\\"encode\\\": {\\n \\\"enter\\\": {\\n \\\"fill\\\": {\\\"value\\\": \\\"black\\\"},\\n \\\"size\\\": {\\\"value\\\": 40}\\n },\\n \\\"update\\\": {\\n \\\"x\\\": {\\\"scale\\\": \\\"xscale\\\", \\\"field\\\": \\\"mean\\\"},\\n \\\"y\\\": {\\\"scale\\\": \\\"yscale\\\", \\\"field\\\": \\\"key\\\", \\\"band\\\": 0.5}\\n }\\n }\\n },\\n {\\n \\\"type\\\": \\\"symbol\\\",\\n \\\"from\\\": {\\\"data\\\": \\\"summary\\\"},\\n \\\"encode\\\": {\\n \\\"enter\\\": {\\n \\\"fill\\\": {\\\"value\\\": \\\"red\\\"},\\n \\\"size\\\": {\\\"value\\\": 40}\\n },\\n \\\"update\\\": {\\n \\\"x\\\": {\\\"scale\\\": \\\"xscale\\\", \\\"field\\\": \\\"max\\\"},\\n \\\"y\\\": {\\\"scale\\\": \\\"yscale\\\", \\\"field\\\": \\\"key\\\", \\\"band\\\": 0.5}\\n }\\n }\\n },\\n {\\n \\\"type\\\": \\\"symbol\\\",\\n \\\"from\\\": {\\\"data\\\": \\\"summary\\\"},\\n \\\"encode\\\": {\\n \\\"enter\\\": {\\n \\\"fill\\\": {\\\"value\\\": \\\"blue\\\"},\\n \\\"size\\\": {\\\"value\\\": 40}\\n },\\n \\\"update\\\": {\\n \\\"x\\\": {\\\"scale\\\": \\\"xscale\\\", \\\"field\\\": \\\"min\\\"},\\n \\\"y\\\": {\\\"scale\\\": \\\"yscale\\\", \\\"field\\\": \\\"key\\\", \\\"band\\\": 0.5}\\n }\\n }\\n }\\n ]\\n}\"},\"aggs\":[]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "22fc47d0-7a19-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "Average Gallons Map",
"visState": "{\"title\":\"Average Gallons Map\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Heatmap\",\"isDesaturated\":true,\"addTooltip\":true,\"heatClusterSize\":1.5,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[0,0],\"wms\":{\"enabled\":false,\"options\":{\"format\":\"image/png\",\"transparent\":true},\"baseLayersAreLoaded\":{},\"tmsLayers\":[{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.3.0&license=5d54f3be-0864-4567-a852-6316da8afd26\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"<p>© <a href=\\\"http://www.openstreetmap.org/copyright\\\">OpenStreetMap</a> contributors | <a href=\\\"https://www.elastic.co/elastic-maps-service\\\">Elastic Maps Service</a></p> \",\"subdomains\":[]}],\"selectedTmsLayer\":{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.3.0&license=5d54f3be-0864-4567-a852-6316da8afd26\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"<p>© <a href=\\\"http://www.openstreetmap.org/copyright\\\">OpenStreetMap</a> contributors | <a href=\\\"https://www.elastic.co/elastic-maps-service\\\">Elastic Maps Service</a></p> \",\"subdomains\":[]}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"VolumeSoldGallons\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"location\",\"autoPrecision\":true,\"isFilteredByCollar\":true,\"useGeocentroid\":true,\"precision\":4}}]}",
"uiStateJSON": "{\"mapZoom\":8,\"mapCenter\":[42.032974332441405,-93.43322753906251]}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"1e94c580-7949-11e8-bd4f-a3db2b4d5b74\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "a5f87f10-7a18-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "ScatterPlot",
"visState": "{\"title\":\"ScatterPlot\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega-lite/v2.json\\n data: {\\n # URL object is a context-aware query to Elasticsearch\\n url: {\\n # The %-enclosed keys are handled by Kibana to modify the query\\n # before it gets sent to Elasticsearch. Context is the search\\n # filter as shown above the dashboard. Timefield uses the value \\n # of the time picker from the upper right corner.\\n %context%: true\\n %timefield%: DateofSale\\n index: iowa-liquor2\\n body: {\\n size: 10000\\n _source: [\\\"DateofSale\\\", \\\"VolumeSoldGallons\\\", \\\"SaleDollars\\\",\\\"CategoryName\\\"]\\n }\\n }\\n # We only need the content of hits.hits array\\n format: {property: \\\"hits.hits\\\"}\\n }\\n # Parse timestamp into a javascript date value\\n transform: [\\n {calculate: \\\"toDate(datum._source['@timestamp'])\\\", as: \\\"time\\\"}\\n ]\\n # Draw a circle, with x being the time field, and y - number of bytes\\n mark: circle\\n encoding: {\\n x: {field: \\\"_source.VolumeSoldGallons\\\", type: \\\"quantitative\\\"}\\n y: {field: \\\"_source.SaleDollars\\\", type: \\\"quantitative\\\"}\\n color: {field:\\\"_source.CategoryName\\\", type:\\\"nominal\\\", legend: {title:\\\"CategoryName\\\"}}\\nshape: {field:\\\"_source.CategoryName\\\", type:\\\"nominal\\\"}\\n }\\n}\"},\"aggs\":[]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "0ef2c8d0-78c6-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "VendorToCategoryDollars-Sankey",
"visState": "{\"title\":\"VendorToCategoryDollars-Sankey\",\"type\":\"vega\",\"params\":{\"spec\":\"{\\n $schema: https://vega.github.io/schema/vega/v3.0.json\\n data: [\\n {\\n // query ES based on the currently selected time range and filter string\\n name: rawData\\n url: {\\n %context%: true\\n %timefield%: DateofSale\\n index: iowa-liquor2\\n body: {\\n size: 0\\n aggs: {\\n table: {\\n composite: {\\n size: 10000\\n sources: [\\n {\\n stk1: {\\n terms: {field: \\\"VendorName.keyword\\\", order: \\\"desc\\\"}\\n }\\n }\\n {\\n stk2: {\\n terms: {field: \\\"CategoryName.keyword\\\", order: \\\"desc\\\"}\\n }\\n }\\n ]\\n }\\n aggs: {\\n sum: {\\n sum: {field: \\\"SaleDollars\\\"}\\n }\\n }\\n }\\n }\\n }\\n }\\n // From the result, take just the data we are interested in\\n format: {property: \\\"aggregations.table.buckets\\\"}\\n // Convert key.stk1 -> stk1 for simpler access below\\n transform: [\\n {\\n type: formula\\n expr: datum.key.stk1\\n as: stk1\\n }\\n {\\n type: formula\\n expr: datum.key.stk2\\n as: stk2\\n }\\n {\\n type: formula\\n expr: datum.sum.value\\n as: size\\n }\\n ]\\n }\\n {\\n name: nodes\\n source: rawData\\n transform: [\\n // when a country is selected, filter out unrelated data\\n {\\n type: filter\\n expr: !groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2\\n }\\n // Set new key for later lookups - identifies each node\\n {\\n type: formula\\n expr: datum.stk1+datum.stk2\\n as: key\\n }\\n // instead of each table row, create two new rows,\\n // one for the source (stack=stk1) and one for destination node (stack=stk2).\\n // The country code stored in stk1 and stk2 fields is placed into grpId field.\\n {\\n type: fold\\n fields: [\\\"stk1\\\", \\\"stk2\\\"]\\n as: [\\\"stack\\\", \\\"grpId\\\"]\\n }\\n // Create a sortkey, different for stk1 and stk2 stacks.\\n {\\n type: formula\\n expr: datum.stack == 'stk1' ? datum.stk1+datum.stk2 : datum.stk2+datum.stk1\\n as: sortField\\n }\\n // Calculate y0 and y1 positions for stacking nodes one on top of the other,\\n // independently for each stack, and ensuring they are in the proper order,\\n // alphabetical from the top (reversed on the y axis)\\n {\\n type: stack\\n groupby: [\\\"stack\\\"]\\n sort: {field: \\\"sortField\\\", order: \\\"descending\\\"}\\n field: size\\n }\\n // calculate vertical center point for each node, used to draw edges\\n {\\n type: formula\\n expr: (datum.y0+datum.y1)/2\\n as: yc\\n }\\n ]\\n }\\n {\\n name: groups\\n source: nodes\\n transform: [\\n // combine all nodes into country groups, summing up the doc counts\\n {\\n type: aggregate\\n groupby: [\\\"stack\\\", \\\"grpId\\\"]\\n fields: [\\\"size\\\"]\\n ops: [\\\"sum\\\"]\\n as: [\\\"total\\\"]\\n }\\n // re-calculate the stacking y0,y1 values\\n {\\n type: stack\\n groupby: [\\\"stack\\\"]\\n sort: {field: \\\"grpId\\\", order: \\\"descending\\\"}\\n field: total\\n }\\n // project y0 and y1 values to screen coordinates\\n // doing it once here instead of doing it several times in marks\\n {\\n type: formula\\n expr: scale('y', datum.y0)\\n as: scaledY0\\n }\\n {\\n type: formula\\n expr: scale('y', datum.y1)\\n as: scaledY1\\n }\\n // boolean flag if the label should be on the right of the stack\\n {\\n type: formula\\n expr: datum.stack == 'stk1'\\n as: rightLabel\\n }\\n // Calculate traffic percentage for this country using \\\"y\\\" scale\\n // domain upper bound, which represents the total traffic\\n {\\n type: formula\\n expr: datum.total/domain('y')[1]\\n as: percentage\\n }\\n ]\\n }\\n {\\n // This is a temp lookup table with all the 'stk2' stack nodes\\n name: destinationNodes\\n source: nodes\\n transform: [\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk2'\\\"}\\n ]\\n }\\n {\\n name: edges\\n source: nodes\\n transform: [\\n // we only want nodes from the left stack\\n {type: \\\"filter\\\", expr: \\\"datum.stack == 'stk1'\\\"}\\n // find corresponding node from the right stack, keep it as \\\"target\\\"\\n {\\n type: lookup\\n from: destinationNodes\\n key: key\\n fields: [\\\"key\\\"]\\n as: [\\\"target\\\"]\\n }\\n // calculate SVG link path between stk1 and stk2 stacks for the node pair\\n {\\n type: linkpath\\n orient: horizontal\\n shape: diagonal\\n sourceY: {expr: \\\"scale('y', datum.yc)\\\"}\\n sourceX: {expr: \\\"scale('x', 'stk1') + bandwidth('x')\\\"}\\n targetY: {expr: \\\"scale('y', datum.target.yc)\\\"}\\n targetX: {expr: \\\"scale('x', 'stk2')\\\"}\\n }\\n // A little trick to calculate the thickness of the line.\\n // The value needs to be the same as the hight of the node, but scaling\\n // size to screen's height gives inversed value because screen's Y\\n // coordinate goes from the top to the bottom, whereas the graph's Y=0\\n // is at the bottom. So subtracting scaled doc count from screen height\\n // (which is the \\\"lower\\\" bound of the \\\"y\\\" scale) gives us the right value\\n {\\n type: formula\\n expr: range('y')[0]-scale('y', datum.size)\\n as: strokeWidth\\n }\\n // Tooltip needs individual link's percentage of all traffic\\n {\\n type: formula\\n expr: datum.size/domain('y')[1]\\n as: percentage\\n }\\n ]\\n }\\n ]\\n scales: [\\n {\\n // calculates horizontal stack positioning\\n name: x\\n type: band\\n range: width\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\n paddingOuter: 0.05\\n paddingInner: 0.95\\n }\\n {\\n // this scale goes up as high as the highest y1 value of all nodes\\n name: y\\n type: linear\\n range: height\\n domain: {data: \\\"nodes\\\", field: \\\"y1\\\"}\\n }\\n {\\n // use rawData to ensure the colors stay the same when clicking.\\n name: color\\n type: ordinal\\n range: category\\n domain: {data: \\\"rawData\\\", field: \\\"stk1\\\"}\\n }\\n {\\n // this scale is used to map internal ids (stk1, stk2) to stack names\\n name: stackNames\\n type: ordinal\\n range: [\\\"Source\\\", \\\"Destination\\\"]\\n domain: [\\\"stk1\\\", \\\"stk2\\\"]\\n }\\n ]\\n axes: [\\n {\\n // x axis should use custom label formatting to print proper stack names\\n orient: bottom\\n scale: x\\n encode: {\\n labels: {\\n update: {\\n text: {scale: \\\"stackNames\\\", field: \\\"value\\\"}\\n }\\n }\\n }\\n }\\n {orient: \\\"left\\\", scale: \\\"y\\\"}\\n ]\\n marks: [\\n {\\n // draw the connecting line between stacks\\n type: path\\n name: edgeMark\\n from: {data: \\\"edges\\\"}\\n // this prevents some autosizing issues with large strokeWidth for paths\\n clip: true\\n encode: {\\n update: {\\n // By default use color of the left node, except when showing traffic\\n // from just one country, in which case use destination color.\\n stroke: [\\n {\\n test: groupSelector && groupSelector.stack=='stk1'\\n scale: color\\n field: stk2\\n }\\n {scale: \\\"color\\\", field: \\\"stk1\\\"}\\n ]\\n strokeWidth: {field: \\\"strokeWidth\\\"}\\n path: {field: \\\"path\\\"}\\n // when showing all traffic, and hovering over a country,\\n // highlight the traffic from that country.\\n strokeOpacity: {\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3\\n }\\n // Ensure that the hover-selected edges show on top\\n zindex: {\\n signal: !groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0\\n }\\n // format tooltip string\\n tooltip: {\\n signal: datum.stk1 + ' → ' + datum.stk2 + ' ' + format(datum.size, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\n }\\n }\\n // Simple mouseover highlighting of a single line\\n hover: {\\n strokeOpacity: {value: 1}\\n }\\n }\\n }\\n {\\n // draw stack groups (countries)\\n type: rect\\n name: groupMark\\n from: {data: \\\"groups\\\"}\\n encode: {\\n enter: {\\n fill: {scale: \\\"color\\\", field: \\\"grpId\\\"}\\n width: {scale: \\\"x\\\", band: 1}\\n }\\n update: {\\n x: {scale: \\\"x\\\", field: \\\"stack\\\"}\\n y: {field: \\\"scaledY0\\\"}\\n y2: {field: \\\"scaledY1\\\"}\\n fillOpacity: {value: 0.6}\\n tooltip: {\\n signal: datum.grpId + ' ' + format(datum.total, ',.0f') + ' (' + format(datum.percentage, '.1%') + ')'\\n }\\n }\\n hover: {\\n fillOpacity: {value: 1}\\n }\\n }\\n }\\n {\\n // draw country code labels on the inner side of the stack\\n type: text\\n from: {data: \\\"groups\\\"}\\n // don't process events for the labels - otherwise line mouseover is unclean\\n interactive: false\\n encode: {\\n update: {\\n // depending on which stack it is, position x with some padding\\n x: {\\n signal: scale('x', datum.stack) + (datum.rightLabel ? bandwidth('x') + 8 : -8)\\n }\\n // middle of the group\\n yc: {signal: \\\"(datum.scaledY0 + datum.scaledY1)/2\\\"}\\n align: {signal: \\\"datum.rightLabel ? 'left' : 'right'\\\"}\\n baseline: {value: \\\"middle\\\"}\\n fontWeight: {value: \\\"bold\\\"}\\n // only show text label if the group's height is large enough\\n text: {\\n signal: abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : ''\\n }\\n }\\n }\\n }\\n {\\n // Create a \\\"show all\\\" button. Shown only when a country is selected.\\n type: group\\n data: [\\n // We need to make the button show only when groupSelector signal is true.\\n // Each mark is drawn as many times as there are elements in the backing data.\\n // Which means that if values list is empty, it will not be drawn.\\n // Here I create a data source with one empty object, and filter that list\\n // based on the signal value. This can only be done in a group.\\n {\\n name: dataForShowAll\\n values: [\\n {}\\n ]\\n transform: [\\n {type: \\\"filter\\\", expr: \\\"groupSelector\\\"}\\n ]\\n }\\n ]\\n // Set button size and positioning\\n encode: {\\n enter: {\\n xc: {signal: \\\"width/2\\\"}\\n y: {value: 30}\\n width: {value: 80}\\n height: {value: 30}\\n }\\n }\\n marks: [\\n {\\n // This group is shown as a button with rounded corners.\\n type: group\\n // mark name allows signal capturing\\n name: groupReset\\n // Only shows button if dataForShowAll has values.\\n from: {data: \\\"dataForShowAll\\\"}\\n encode: {\\n enter: {\\n cornerRadius: {value: 6}\\n fill: {value: \\\"#f5f5f5\\\"}\\n stroke: {value: \\\"#c1c1c1\\\"}\\n strokeWidth: {value: 2}\\n // use parent group's size\\n height: {\\n field: {group: \\\"height\\\"}\\n }\\n width: {\\n field: {group: \\\"width\\\"}\\n }\\n }\\n update: {\\n // groups are transparent by default\\n opacity: {value: 1}\\n }\\n hover: {\\n opacity: {value: 0.7}\\n }\\n }\\n marks: [\\n {\\n type: text\\n // if true, it will prevent clicking on the button when over text.\\n interactive: false\\n encode: {\\n enter: {\\n // center text in the paren group\\n xc: {\\n field: {group: \\\"width\\\"}\\n mult: 0.5\\n }\\n yc: {\\n field: {group: \\\"height\\\"}\\n mult: 0.5\\n offset: 2\\n }\\n align: {value: \\\"center\\\"}\\n baseline: {value: \\\"middle\\\"}\\n fontWeight: {value: \\\"bold\\\"}\\n text: {value: \\\"Show All\\\"}\\n }\\n }\\n }\\n ]\\n }\\n ]\\n }\\n ]\\n signals: [\\n {\\n // used to highlight traffic to/from the same country\\n name: groupHover\\n value: {}\\n on: [\\n {\\n events: @groupMark:mouseover\\n update: \\\"{stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\n }\\n {events: \\\"mouseout\\\", update: \\\"{}\\\"}\\n ]\\n }\\n // used to filter only the data related to the selected country\\n {\\n name: groupSelector\\n value: false\\n on: [\\n {\\n // Clicking groupMark sets this signal to the filter values\\n events: @groupMark:click!\\n update: \\\"{stack:datum.stack, stk1:datum.stack=='stk1' && datum.grpId, stk2:datum.stack=='stk2' && datum.grpId}\\\"\\n }\\n {\\n // Clicking \\\"show all\\\" button, or double-clicking anywhere resets it\\n events: [\\n {type: \\\"click\\\", markname: \\\"groupReset\\\"}\\n {type: \\\"dblclick\\\"}\\n ]\\n update: \\\"false\\\"\\n }\\n ]\\n }\\n ]\\n}\"},\"aggs\":[]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "4d008190-7a19-11e8-bd4f-a3db2b4d5b74",
"_type": "dashboard",
"_source": {
"title": "Liquor",
"hits": 0,
"description": "",
"panelsJSON": "[{\"panelIndex\":\"1\",\"gridData\":{\"x\":0,\"y\":42,\"w\":24,\"h\":15,\"i\":\"1\"},\"embeddableConfig\":{},\"id\":\"e06b02c0-78c5-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"2\",\"gridData\":{\"x\":24,\"y\":42,\"w\":24,\"h\":15,\"i\":\"2\"},\"embeddableConfig\":{},\"id\":\"0ef2c8d0-78c6-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"3\",\"gridData\":{\"x\":0,\"y\":57,\"w\":24,\"h\":15,\"i\":\"3\"},\"embeddableConfig\":{},\"id\":\"0116b7f0-7a18-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"4\",\"gridData\":{\"x\":0,\"y\":27,\"w\":24,\"h\":15,\"i\":\"4\"},\"embeddableConfig\":{},\"id\":\"a5f87f10-7a18-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"5\",\"gridData\":{\"x\":24,\"y\":57,\"w\":24,\"h\":15,\"i\":\"5\"},\"embeddableConfig\":{},\"id\":\"f4ba6460-7a18-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"6\",\"gridData\":{\"x\":24,\"y\":27,\"w\":24,\"h\":15,\"i\":\"6\"},\"embeddableConfig\":{\"mapCenter\":[42.09822241118974,-93.4771728515625],\"mapZoom\":7},\"id\":\"22fc47d0-7a19-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"7\",\"gridData\":{\"x\":24,\"y\":12,\"w\":24,\"h\":15,\"i\":\"7\"},\"embeddableConfig\":{\"mapCenter\":[42.07376224008722,-93.46618652343751],\"mapZoom\":7},\"id\":\"37a0b450-7a19-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"8\",\"gridData\":{\"x\":0,\"y\":12,\"w\":24,\"h\":15,\"i\":\"8\"},\"embeddableConfig\":{},\"id\":\"70964ef0-7a19-11e8-bd4f-a3db2b4d5b74\",\"type\":\"visualization\",\"version\":\"6.3.0\"},{\"panelIndex\":\"9\",\"gridData\":{\"x\":1,\"y\":0,\"w\":47,\"h\":12,\"i\":\"9\"},\"version\":\"6.3.0\",\"type\":\"visualization\",\"id\":\"d5364320-7a22-11e8-bd4f-a3db2b4d5b74\",\"embeddableConfig\":{}}]",
"optionsJSON": "{\"darkTheme\":false,\"hidePanelTitles\":false,\"useMargins\":true}",
"version": 1,
"timeRestore": true,
"timeTo": "Fri Jun 01 2018 00:00:00 GMT-0500",
"timeFrom": "Sun Jan 01 2012 00:00:00 GMT-0600",
"refreshInterval": {
"display": "Off",
"pause": false,
"value": 0
},
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"query\":{\"language\":\"lucene\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "37a0b450-7a19-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "Count Map",
"visState": "{\"title\":\"Count Map\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Heatmap\",\"isDesaturated\":true,\"addTooltip\":true,\"heatClusterSize\":1.5,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[0,0],\"wms\":{\"enabled\":false,\"options\":{\"format\":\"image/png\",\"transparent\":true},\"baseLayersAreLoaded\":{},\"tmsLayers\":[{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.3.0&license=5d54f3be-0864-4567-a852-6316da8afd26\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"<p>© <a href=\\\"http://www.openstreetmap.org/copyright\\\">OpenStreetMap</a> contributors | <a href=\\\"https://www.elastic.co/elastic-maps-service\\\">Elastic Maps Service</a></p> \",\"subdomains\":[]}],\"selectedTmsLayer\":{\"id\":\"road_map\",\"url\":\"https://tiles.maps.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=6.3.0&license=5d54f3be-0864-4567-a852-6316da8afd26\",\"minZoom\":0,\"maxZoom\":18,\"attribution\":\"<p>© <a href=\\\"http://www.openstreetmap.org/copyright\\\">OpenStreetMap</a> contributors | <a href=\\\"https://www.elastic.co/elastic-maps-service\\\">Elastic Maps Service</a></p> \",\"subdomains\":[]}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"location\",\"autoPrecision\":true,\"isFilteredByCollar\":true,\"useGeocentroid\":true,\"precision\":4}}]}",
"uiStateJSON": "{\"mapZoom\":8,\"mapCenter\":[42.032974332441405,-93.43322753906251]}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"1e94c580-7949-11e8-bd4f-a3db2b4d5b74\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "d5364320-7a22-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "Sale Dollars over time",
"visState": "{\"title\":\"Sale Dollars over time\",\"type\":\"histogram\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Average SaleDollars\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"line\",\"mode\":\"normal\",\"data\":{\"label\":\"Average SaleDollars\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true},{\"show\":true,\"mode\":\"normal\",\"type\":\"histogram\",\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"data\":{\"id\":\"3\",\"label\":\"Percentiles of SaleDollars\"},\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"SaleDollars\"}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"DateofSale\",\"interval\":\"custom\",\"customInterval\":\"2w\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"enabled\":true,\"type\":\"percentiles\",\"schema\":\"metric\",\"params\":{\"field\":\"SaleDollars\",\"percents\":[50,90]}}]}",
"uiStateJSON": "{\"vis\":{\"legendOpen\":false}}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"1e94c580-7949-11e8-bd4f-a3db2b4d5b74\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "70964ef0-7a19-11e8-bd4f-a3db2b4d5b74",
"_type": "visualization",
"_source": {
"title": "Category Breakdown",
"visState": "{\"title\":\"Category Breakdown\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"CategoryName.keyword\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"size\":50,\"order\":\"desc\",\"orderBy\":\"1\"}}]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"1e94c580-7949-11e8-bd4f-a3db2b4d5b74\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"_meta": {
"savedObjectVersion": 2
}
}
]