CustomPieChart
CustomPieChart
An example showing how to render a basic pie chart.
module CustomPieChart exposing (main)
import Visualization.Shape as Shape exposing (defaultPieConfig)
import Array exposing (Array)
import Svg exposing (Svg, svg, g, path, text_, text)
import Svg.Attributes exposing (transform, d, style, dy, width, height, textAnchor)
import Html exposing (Html, div, input, label, br, h2)
import Html.Attributes exposing (type_, value, step)
import Html.Events exposing (onInput)
import String
screenWidth : Float
screenWidth =
990
screenHeight : Float
screenHeight =
504
colors : Array String
colors =
Array.fromList [ "#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00" ]
radius : Float
radius =
min screenWidth screenHeight / 2
type alias ChartConfig =
{ outerRadius : Float
, innerRadius : Float
, padAngle : Float
, cornerRadius : Float
, labelPosition : Float
}
type Msg
= UpdateOuterRadius String
| UpdateInnerRadius String
| UpdatePadAngle String
| UpdateCornerRadius String
| UpdateLabelPosition String
type alias Model =
{ config : ChartConfig
, data : List ( String, Float )
}
drawChart : ChartConfig -> List ( String, Float ) -> Svg msg
drawChart config model =
let
pieData =
model
|> List.map Tuple.second
|> Shape.pie
{ defaultPieConfig
| innerRadius = config.innerRadius
, outerRadius = config.outerRadius
, padAngle = config.padAngle
, cornerRadius = config.cornerRadius
, sortingFn = \_ _ -> EQ
}
makeSlice index datum =
path [ d (Shape.arc datum), style ("fill:" ++ (Maybe.withDefault "#000" <| Array.get index colors) ++ "; stroke: #fff;") ] []
makeLabel slice ( label, value ) =
text_
[ transform ("translate" ++ toString (Shape.centroid { slice | innerRadius = config.labelPosition, outerRadius = config.labelPosition }))
, dy ".35em"
, textAnchor "middle"
]
[ text label ]
in
svg [ width (toString (radius * 2) ++ "px"), height (toString (radius * 2) ++ "px") ]
[ g [ transform ("translate(" ++ toString radius ++ "," ++ toString radius ++ ")") ]
[ g [] <| List.indexedMap makeSlice pieData
, g [] <| List.map2 makeLabel pieData model
]
]
update : Msg -> Model -> Model
update msg model =
let
config =
model.config
in
case msg of
UpdateOuterRadius amount ->
{ model | config = { config | outerRadius = Result.withDefault 0 <| String.toFloat amount } }
UpdateInnerRadius amount ->
{ model | config = { config | innerRadius = Result.withDefault 0 <| String.toFloat amount } }
UpdatePadAngle amount ->
{ model | config = { config | padAngle = Result.withDefault 0 <| String.toFloat amount } }
UpdateCornerRadius amount ->
{ model | config = { config | cornerRadius = Result.withDefault 0 <| String.toFloat amount } }
UpdateLabelPosition amount ->
{ model | config = { config | labelPosition = Result.withDefault 0 <| String.toFloat amount } }
view : Model -> Html Msg
view model =
div [ style "display: flex; justify-content: space-around" ]
[ drawChart model.config model.data
, div []
[ h2 [] [ text "Pie Configuration" ]
, label [] [ text "Outer Radius" ]
, input [ type_ "range", onInput UpdateOuterRadius, value (toString model.config.outerRadius), Html.Attributes.min "0", Html.Attributes.max (toString radius) ] []
, text (toString model.config.outerRadius)
, br [] []
, label [] [ text "Inner Radius" ]
, input [ type_ "range", onInput UpdateInnerRadius, value (toString model.config.innerRadius), Html.Attributes.min "0", Html.Attributes.max (toString radius) ] []
, text (toString model.config.innerRadius)
, br [] []
, label [] [ text "Pad Angle" ]
, input [ type_ "range", onInput UpdatePadAngle, value (toString model.config.padAngle), Html.Attributes.min "0", Html.Attributes.max "0.8", step "0.01" ] []
, text (toString model.config.padAngle)
, br [] []
, label [] [ text "Corner Radius" ]
, input [ type_ "range", onInput UpdateCornerRadius, value (toString model.config.cornerRadius), Html.Attributes.min "0", Html.Attributes.max "20", step "0.25" ] []
, text (toString model.config.cornerRadius)
, br [] []
, label [] [ text "Label Position" ]
, input [ type_ "range", onInput UpdateLabelPosition, value (toString model.config.labelPosition), Html.Attributes.min "0", Html.Attributes.max (toString radius) ] []
, text (toString model.config.labelPosition)
, br [] []
]
]
data : List ( String, Float )
data =
[ ( "<5", 2704659 )
, ( "5-13", 4499890 )
, ( "14-17", 2159981 )
, ( "18-24", 3853788 )
, ( "25-44", 14106543 )
, ( "45-64", 8819342 )
, ( "≥65", 612463 )
]
model : { config : ChartConfig, data : List ( String, Float ) }
model =
{ config =
{ outerRadius = 210
, innerRadius = 200
, padAngle = 0.02
, cornerRadius = 20
, labelPosition = 230
}
, data = data
}
main : Program Never { config : ChartConfig, data : List ( String, Float ) } Msg
main =
Html.beginnerProgram
{ model = model
, update = update
, view = view
}