Skip to content

Commit 43e6da8

Browse files
authored
Adds funnel chart (#168)
1 parent 10ba741 commit 43e6da8

File tree

3 files changed

+210
-13
lines changed

3 files changed

+210
-13
lines changed

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ![Elm-visualization](https://code.gampleman.eu/elm-visualization/misc/Logo-600.png)
22

3-
[Tutorial](https://github.com/gampleman/elm-visualization/blob/master/docs/INTRO.md) | [Docs](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/) | [Examples](https://elm-visualization.netlify.app/) | [GitHub](https://github.com/gampleman/elm-visualization) | [Changelog](https://github.com/gampleman/elm-visualization/releases) | `#visualization` on [Elm slack](https://elmlang.herokuapp.com)
3+
[Tutorial](https://github.com/gampleman/elm-visualization/blob/master/docs/INTRO.md) | [Docs](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/) | [Examples](https://elm-visualization.netlify.app/) | [GitHub](https://github.com/gampleman/elm-visualization) | [Changelog](https://github.com/gampleman/elm-visualization/releases) | `#visualization` on [Elm slack](https://elmlang.herokuapp.com)
44

55
This project is designed to give you all the tools needed to build data visualizations.
66
It is not a charting library in the sense that you have pre-bundled Excel-style
@@ -34,52 +34,52 @@ You can use [this Ellie](https://ellie-app.com/p6X5hXxcdRCa1) to run the example
3434

3535
## What's included?
3636

37-
### [Scales](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Scale/)
37+
### [Scales](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Scale/)
3838

3939
Most of the time you have data that has properties that you want to display on the
4040
screen, however these properties typically aren't in pixels. Scales solve this
4141
fundamental problem by giving you convenient ways to transform raw data into positions,
4242
sizes, colors, labels and other ways to display data.
4343

44-
### [Axis](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Axis/)
44+
### [Axis](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Axis/)
4545

4646
A component that allows you to visualize a Scale. Those little ticks that describe
4747
the dimensions of a plot.
4848

49-
### [Shapes](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Shape/)
49+
### [Shapes](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Shape/)
5050

5151
This module gives you ways to draw some fundamental shapes used in data visualization, including lines (as in line or area charts),
5252
as well as arcs (as in pie charts).
5353

54-
### [Force Layout](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Force/)
54+
### [Force Layout](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Force/)
5555

5656
Use a simulation of physical forces to do layout. Suitable for i.e. network graphs.
5757

58-
### [Hierarchy](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Hierarchy/)
58+
### [Hierarchy](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Hierarchy/)
5959

6060
Layout algorithms for dealing with trees.
6161

62-
### [Interpolation](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Interpolation/)
62+
### [Interpolation](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Interpolation/)
6363

6464
Smoothly transition between pairs of values. Useful for animation, or generating gradients of values.
6565

66-
### [Transition](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Transition/)
66+
### [Transition](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Transition/)
6767

6868
Build complex animations using Interpolation.
6969

70-
### [Histogram](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Histogram/)
70+
### [Histogram](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Histogram/)
7171

7272
Compute histograms of data.
7373

74-
### [Brush](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Brush/)
74+
### [Brush](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Brush/)
7575

7676
Interactively select subregions of a dataset.
7777

78-
### [Zoom](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Zoom/)
78+
### [Zoom](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Zoom/)
7979

8080
Build pan and zoom user interactions.
8181

82-
### [Statistics](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Statistics/)
82+
### [Statistics](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Statistics/)
8383

8484
Process data to extract useful insights for visualizations.
8585

elm.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "gampleman/elm-visualization",
44
"summary": "A data visualization package for Elm",
55
"license": "MIT",
6-
"version": "2.4.1",
6+
"version": "2.4.2",
77
"exposed-modules": [
88
"Scale",
99
"Scale.Color",

examples/FunnelChart.elm

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
module FunnelChart exposing (main)
2+
3+
{-| Shows how to build a Funnel chart. It is quite similar to a bar chart, but with
4+
the addition of an extra shape connecting the bars.
5+
6+
The design here follows [this great best practices guide](https://www.atlassian.com/data/charts/funnel-chart-complete-guide).
7+
8+
@category Basics
9+
10+
-}
11+
12+
import Color exposing (Color)
13+
import Float.Extra
14+
import List.Extra
15+
import Scale exposing (BandScale, ContinuousScale, SequentialScale, defaultBandConfig)
16+
import Scale.Color
17+
import TypedSvg exposing (g, polygon, rect, svg, text_)
18+
import TypedSvg.Attributes exposing (fill, fillOpacity, fontFamily, points, stroke, textAnchor, transform, viewBox)
19+
import TypedSvg.Attributes.InPx exposing (fontSize, height, strokeWidth, width, x, y)
20+
import TypedSvg.Core exposing (Svg, text)
21+
import TypedSvg.Types exposing (AnchorAlignment(..), Opacity(..), Paint(..), Transform(..))
22+
23+
24+
w : Float
25+
w =
26+
900
27+
28+
29+
h : Float
30+
h =
31+
450
32+
33+
34+
titleWidth : Float
35+
titleWidth =
36+
100
37+
38+
39+
padding : Float
40+
padding =
41+
30
42+
43+
44+
type alias Datum =
45+
{ title : String
46+
, value : Float
47+
}
48+
49+
50+
type Shape
51+
= Bar Int Datum
52+
| Connector Datum Datum
53+
54+
55+
56+
-- Here we preprocess the data to create the shapes we want to draw in an interleaved list
57+
-- [Datum "A" 1, Datum "B" 2, Datum "C" 3]
58+
-- -> [ Bar 0 (Datum "A" 1), Connector (Datum "A" 1) (Datum "B" 2),
59+
-- Bar 1 (Datum "B" 2), Connector (Datum "B" 2) (Datum "C" 3),
60+
-- Bar 2 (Datum "C" 3) ]
61+
62+
63+
preprocessed : List Shape
64+
preprocessed =
65+
List.map2 Connector data (List.drop 1 data)
66+
|> List.Extra.interweave (List.indexedMap Bar data)
67+
68+
69+
maxValue : Float
70+
maxValue =
71+
data
72+
|> List.map .value
73+
|> List.maximum
74+
|> Maybe.withDefault 0
75+
76+
77+
xScale : ContinuousScale Float
78+
xScale =
79+
Scale.linear ( 0, w - 2 * padding - 2 * titleWidth ) ( 0, maxValue )
80+
81+
82+
yScale : BandScale String
83+
yScale =
84+
data
85+
|> List.map .title
86+
|> Scale.band { defaultBandConfig | paddingInner = 0.2 } ( padding, h - padding )
87+
88+
89+
colorScale : SequentialScale Color
90+
colorScale =
91+
Scale.sequential Scale.Color.bluesInterpolator ( -1, toFloat (List.length data - 1) )
92+
93+
94+
view : Svg msg
95+
view =
96+
List.map
97+
(\shape ->
98+
case shape of
99+
Bar index datum ->
100+
let
101+
color =
102+
Scale.convert colorScale (toFloat index)
103+
in
104+
g []
105+
[ rect
106+
[ x (-(Scale.convert xScale datum.value) / 2)
107+
, y (Scale.convert yScale datum.title)
108+
, width (Scale.convert xScale datum.value)
109+
, height (Scale.bandwidth yScale)
110+
, fill (Paint color)
111+
]
112+
[]
113+
, text_
114+
[ x (-w / 2 + padding)
115+
, y (Scale.convert yScale datum.title + Scale.bandwidth yScale / 2)
116+
, transform [ Translate 0 5 ]
117+
]
118+
[ text datum.title ]
119+
, text_
120+
[ x (w / 2 - padding)
121+
, y (Scale.convert yScale datum.title + Scale.bandwidth yScale / 2)
122+
, transform [ Translate 0 5 ]
123+
, textAnchor AnchorEnd
124+
]
125+
[ text (Float.Extra.toFixedDecimalPlaces 1 (100 * datum.value / maxValue) ++ "%") ]
126+
, text_
127+
[ x 0
128+
, y (Scale.convert yScale datum.title + Scale.bandwidth yScale / 2)
129+
, transform [ Translate 0 5 ]
130+
, textAnchor AnchorMiddle
131+
, fill
132+
(Paint
133+
-- This is a simple way to make sure the text
134+
-- is readable
135+
-- However, you may want a more sophisticated
136+
-- algorithm for other color scales
137+
(if (Color.toHsla color).lightness > 0.5 then
138+
Color.black
139+
140+
else
141+
Color.white
142+
)
143+
)
144+
, stroke (Paint color)
145+
, strokeWidth 3
146+
, TypedSvg.Core.attribute "paint-order" "stroke"
147+
, fillOpacity (Opacity 0.9)
148+
]
149+
[ text (String.fromFloat datum.value) ]
150+
]
151+
152+
Connector top bottom ->
153+
g []
154+
[ polygon
155+
[ points
156+
[ ( -(Scale.convert xScale top.value) / 2, Scale.convert yScale top.title + Scale.bandwidth yScale )
157+
, ( Scale.convert xScale top.value / 2, Scale.convert yScale top.title + Scale.bandwidth yScale )
158+
, ( Scale.convert xScale bottom.value / 2, Scale.convert yScale bottom.title )
159+
, ( -(Scale.convert xScale bottom.value) / 2, Scale.convert yScale bottom.title )
160+
]
161+
, fill (Paint (Scale.convert colorScale -1))
162+
]
163+
[]
164+
, text_
165+
[ x 0
166+
, y (Scale.convert yScale top.title + Scale.bandwidth yScale)
167+
, transform [ Translate 0 14 ]
168+
, textAnchor AnchorMiddle
169+
, fontSize 12
170+
, fillOpacity (Opacity 0.7)
171+
]
172+
[ text (Float.Extra.toFixedDecimalPlaces 1 (100 * bottom.value / top.value) ++ "%") ]
173+
]
174+
)
175+
preprocessed
176+
|> svg [ viewBox (-w / 2) 0 w h, fontFamily [ "sans-serif" ] ]
177+
178+
179+
data : List Datum
180+
data =
181+
[ { title = "Total"
182+
, value = 54809
183+
}
184+
, { title = "Helpful"
185+
, value = 29434
186+
}
187+
, { title = "Qualified"
188+
, value = 10345
189+
}
190+
, { title = "Successful"
191+
, value = 3432
192+
}
193+
]
194+
195+
196+
main =
197+
view

0 commit comments

Comments
 (0)