Current section
Files
Jump to
Current section
Files
src/scomps/scomp_base_google_chart.erl
%% @author Marc Worrell <marc@worrell.nl>
%% @copyright 2009 Marc Worrell
%%
%% Based on code (c) 2008-2009 Rusty Klophaus
%%
%% @doc Generate a google chart for a dataset
%% Copyright 2009-2010 Marc Worrell, Konstantin Nikiforov
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(scomp_base_google_chart).
-behaviour(zotonic_scomp).
-export([vary/2, render/3]).
-include_lib("zotonic_core/include/zotonic.hrl").
vary(_Params, _Context) -> default.
render(Params, _Vars, Context) ->
Class = proplists:get_value(class, Params),
Style = proplists:get_value(style, Params),
Id = proplists:get_value(id, Params),
Title = proplists:get_value(title, Params),
Color = proplists:get_value(color, Params, "909090"),
FontSize = z_convert:to_integer(proplists:get_value(font_size, Params, 10)),
Width = z_convert:to_integer(proplists:get_value(width, Params, 300)),
Height = z_convert:to_integer(proplists:get_value(height, Params, 150)),
GridX = proplists:get_value(grid_x, Params),
GridY = proplists:get_value(grid_y, Params),
GridLineLength = z_convert:to_integer(proplists:get_value(grid_line_length, Params, 1)),
GridBlankLength = z_convert:to_integer(proplists:get_value(grid_blank_length, Params, 5)),
BGColor = proplists:get_value(background_color, Params, "ffffff"),
ChartColor = proplists:get_value(chart_color, Params, "ffffff"),
LegendLoc = proplists:get_value(legend_location, Params, "bottom"),
AxesArg = proplists:get_all_values(axis, Params),
DataArg = proplists:get_all_values(data, Params),
BarSpace = z_convert:to_integer(proplists:get_value(bar_space, Params, 3)),
BarGroupSpace = z_convert:to_integer(proplists:get_value(bar_group_space, Params, 7)),
AxesArg1 = lists:flatten(AxesArg),
DataArg1 = lists:flatten(DataArg),
% Path to Google API
Path = "https://chart.apis.google.com/chart?",
% Chart Type...
Type = [
"&cht=",
case proplists:get_value(type, Params, "line") of
"line" -> "lc";
"sparkline" -> "ls";
"stacked_horizontal_bar" -> "bhs";
"stacked_vertical_bar" -> "bvs";
"grouped_horizontal_bar" -> "bhg";
"grouped_vertical_bar" -> "bvg";
"pie" -> "p";
"pie3d" -> "p3";
OtherType -> z_url:url_encode(OtherType)
end
],
% Title...
TitleText = case Title of
undefined -> [];
[] -> [];
<<>> -> <<>>;
OtherTitle -> ["&chtt=", z_url:url_encode(OtherTitle)]
end,
% Title Color and Font Size...
TitleStyle = io_lib:format("&chts=~s,~b", [z_convert:to_list(Color), FontSize]),
% Size...
Size = io_lib:format("&chs=~bx~b", [Width, Height]),
% Grid...
Grid = io_lib:format("&chg=~s,~s,~b,~b", [
z_url:url_encode(z_convert:to_list(z_utils:coalesce([GridX, 0]))),
z_url:url_encode(z_convert:to_list(z_utils:coalesce([GridY, 0]))),
GridLineLength,
GridBlankLength
]),
% Background Colors...
BGColors = io_lib:format("&chf=bg,s,~s|c,s,~s", [
z_url:url_encode(z_convert:to_list(BGColor)),
z_url:url_encode(z_convert:to_list(ChartColor))
]),
% Legend Location...
LegendLocation = "&chdlp="
++ case LegendLoc of
"top" -> "t";
"left" -> "l";
"bottom" -> "b";
"right" -> "r";
_ -> "r"
end,
% Axes...
Axes = case AxesArg1 of
[] -> [];
AxesRecords ->
ProcessedAxes = [process_axis(N - 1, lists:nth(N, AxesRecords), Context) || N <- lists:seq(1, length(AxesRecords))],
AxesPositions0 = string:join([X || [X, _, _] <- ProcessedAxes], ","),
AxesScaling0 = [X || [_, X, _] <- ProcessedAxes],
AxesColors0 = string:join([X || [_, _, X] <- ProcessedAxes], "|"),
AxesPositions = "&chxt=" ++ z_convert:to_list( z_url:url_encode(AxesPositions0) ),
AxesScaling = AxesScaling0, % Already urlencode in process_axis/2
AxesColors = "&chxs=" ++ z_convert:to_list( z_url:url_encode(AxesColors0) ),
AxesPositions ++ AxesScaling ++ AxesColors
end,
% Data...
{MaxValueLength, Data} = case DataArg1 of
[] -> {0, []};
DataRecords ->
ProcessedData = [process_data(N -1, lists:nth(N, DataRecords)) || N <- lists:seq(1, length(DataRecords))],
DataColors0 = string:join([X || [X, _, _, _, _, _] <- ProcessedData], ","),
DataLegends0 = string:join([X || [_, X, _, _, _, _] <- ProcessedData], "|"),
DataScales0 = string:join([X || [_, _, X, _, _, _] <- ProcessedData], ","),
DataStyles0 = string:join([X || [_, _, _, X, _, _] <- ProcessedData], "|"),
DataValues0 = string:join([X || [_, _, _, _, X, _] <- ProcessedData], "|"),
DataColors = "&chco=" ++ z_convert:to_list( z_url:url_encode(DataColors0) ),
DataLegends = "&chdl=" ++ z_convert:to_list( z_url:url_encode(DataLegends0) ),
DataScales = "&chds=" ++ z_convert:to_list( z_url:url_encode(DataScales0) ),
DataStyles = "&chls=" ++ z_convert:to_list( z_url:url_encode(DataStyles0) ),
DataValues = "&chd=t:" ++ z_convert:to_list( z_url:url_encode(DataValues0) ),
DataLegends1 = case string:strip(DataLegends, both, $|) of
"&chdl=" -> [];
_ -> DataLegends
end,
{lists:max([X || [_, _, _, _, _, X] <- ProcessedData]),
DataColors ++ DataLegends1 ++ DataScales ++ DataValues ++ DataStyles}
end,
% Calculate bar size...
BarSize = case MaxValueLength of
0 ->
[];
_ ->
DataGroupsLength = length(Data),
GroupSpacerPixels = MaxValueLength * BarGroupSpace,
BarSpacerPixels = MaxValueLength * (DataGroupsLength * BarSpace),
AvailablePixels = case proplists:get_value(type, Params, "line") of
"stacked_horizontal_bar" -> Height;
"grouped_horizontal_bar" -> Height;
"stacked_vertical_bar" -> Width;
"grouped_vertical_bar" -> Width;
_ -> 0
end,
IndividualBarSize = (AvailablePixels - GroupSpacerPixels - BarSpacerPixels) / (DataGroupsLength * MaxValueLength),
io_lib:format("&chbh=~b,~b,~b", [trunc(IndividualBarSize), BarSpace, BarGroupSpace])
end,
ImageUri = iolist_to_binary([
Path, Type, TitleText, TitleStyle, Size, Grid,
BGColors, LegendLocation, BarSize, Axes, Data
]),
{ok, z_tags:render_tag(
<<"img">>,
[
{<<"id">>, z_html:escape(Id)},
{<<"name">>, z_html:escape(Id)},
{<<"class">>, [<<"google_chart">>, Class]},
{<<"alt">>, <<"google chart">>},
{<<"style">>, z_html:escape(Style)},
{<<"src">>, ImageUri},
{<<"width">>, Width},
{<<"height">>, Height}
])}.
process_axis(N, {axis, Axis}, Context) ->
FontSize = proplists:get_value(font_size, Axis, 10),
Color = proplists:get_value(color, Axis, "909090"),
Position = case z_convert:to_list(proplists:get_value(position, Axis, "top")) of
"top" -> "t";
"right" -> "r";
"bottom" -> "x";
"left" -> "y";
OtherPosition -> erlang:error({unknown_axis_position, OtherPosition})
end,
Style = lists:flatten(io_lib:format("~b,~s,~b", [N, z_convert:to_list(Color), FontSize])),
Scaling = case {proplists:get_value(labels, Axis, []), proplists:get_value(range, Axis, [])} of
{LabelsP, []} when is_list(LabelsP) ->
StringLabels = [make_label(X, Context) || X <- LabelsP],
Labels = integer_to_list(N) ++ ":|" ++ string:join(StringLabels, "|"),
"&chxl=" ++ z_url:url_encode(Labels);
{[], Range} when is_list(Range) ->
Format = string:join(["~p" || _X <- [c | Range] ], ","),
"&chxr=" ++ z_url:url_encode(
lists:flatten(io_lib:format(Format, [N | Range])));
_ ->
""
end,
[Position, Scaling, Style].
process_data(_N, {data, Data}) ->
LineWidth = proplists:get_value(line_width, Data, 1),
LineLength = proplists:get_value(line_length, Data, 1),
BlankLength = proplists:get_value(blank_length, Data, 0),
MinValue = proplists:get_value(min_value, Data, 0),
MaxValue = proplists:get_value(max_value, Data, 100),
Color = proplists:get_value(color, Data),
Legend = z_convert:to_list(proplists:get_value(legend, Data)),
Values = proplists:get_value(values, Data, []),
Scale = lists:flatten(io_lib:format("~b,~b", [MinValue,MaxValue])),
StringValues = [z_convert:to_list(X) || X <- Values],
JoinedValues = string:join(StringValues, ","),
Styles = lists:flatten(io_lib:format("~b,~b,~b", [LineWidth, LineLength, BlankLength])),
[flatten_color(Color), Legend, Scale, Styles, JoinedValues, length(StringValues)].
flatten_color([A|_] = List) when is_list(A) or is_binary(A) or is_atom(A) ->
string:join([ z_convert:to_list(Color) || Color <- List ], ",");
flatten_color(A) ->
z_convert:to_list(A).
make_label(N, _Context) when is_integer(N) orelse is_float(N) orelse is_atom(N) ->
z_convert:to_list(N);
make_label(L, Context) ->
z_convert:to_list(?__(L, Context)).