From e735797037623dc26f57da3a1150e600ed3003b1 Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Fri, 3 Feb 2017 11:26:53 -0800 Subject: [PATCH] adding validation. --- _examples/twopoint/main.go | 72 ++++++++++++++++++++++++++++++++++ _examples/twopoint/output.png | Bin 0 -> 41022 bytes annotation_series.go | 13 +++++- bollinger_band_series.go | 13 +++++- chart.go | 11 ++++++ concat_series.go | 12 ++++++ continuous_series.go | 14 +++++++ continuous_series_test.go | 23 +++++++++++ ema_series.go | 10 +++++ histogram_series.go | 10 +++++ image_writer.go | 2 +- linear_regression_series.go | 10 +++++ macd_series.go | 10 +++++ min_max_series.go | 21 +++++++++- pie_chart.go | 2 +- series.go | 1 + sma_series.go | 10 +++++ stacked_bar_chart.go | 2 +- time_series.go | 17 +++++++- time_series_test.go | 39 ++++++++++++++++++ 20 files changed, 285 insertions(+), 7 deletions(-) create mode 100644 _examples/twopoint/main.go create mode 100644 _examples/twopoint/output.png diff --git a/_examples/twopoint/main.go b/_examples/twopoint/main.go new file mode 100644 index 0000000..d703431 --- /dev/null +++ b/_examples/twopoint/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "bytes" + "log" + "os" + //"time" + "github.com/wcharczuk/go-chart" //exposes "chart" +) + +func main() { + + var b float64 + b = 1000 + + ts1 := chart.ContinuousSeries{ //TimeSeries{ + Name: "Time Series", + Style: chart.Style{ + Show: true, + }, + + //XValues: []time.Time{time.Unix(3*b,0),time.Unix(4*b,0),time.Unix(5*b,0),time.Unix(6*b,0),time.Unix(7*b,0),time.Unix(8*b,0),time.Unix(9*b,0),time.Unix(10*b,0)}, + XValues: []float64{10 * b, 20 * b, 30 * b, 40 * b, 50 * b, 60 * b, 70 * b, 80 * b}, + YValues: []float64{1.0, 2.0, 30.0, 4.0, 50.0, 6.0, 7.0, 88.0}, + } + + ts2 := chart.ContinuousSeries{ //TimeSeries{ + Style: chart.Style{ + Show: true, + StrokeColor: chart.GetDefaultColor(1), + }, + + XValues: []float64{10 * b, 20 * b, 30 * b, 40 * b, 50 * b, 60 * b, 70 * b, 80 * b}, + YValues: []float64{15.0, 52.0, 30.0, 42.0, 50.0, 26.0, 77.0, 38.0}, + } + + graph := chart.Chart{ + + XAxis: chart.XAxis{ + Name: "The XAxis", + NameStyle: chart.StyleShow(), + Style: chart.StyleShow(), + ValueFormatter: chart.TimeMinuteValueFormatter, //TimeHourValueFormatter, + }, + + YAxis: chart.YAxis{ + Name: "The YAxis", + NameStyle: chart.StyleShow(), + Style: chart.StyleShow(), + }, + + Series: []chart.Series{ + ts1, + ts2, + }, + } + + buffer := bytes.NewBuffer([]byte{}) + err := graph.Render(chart.PNG, buffer) + if err != nil { + log.Fatal(err) + } + + fo, err := os.Create("output.png") + if err != nil { + panic(err) + } + + if _, err := fo.Write(buffer.Bytes()); err != nil { + panic(err) + } +} diff --git a/_examples/twopoint/output.png b/_examples/twopoint/output.png new file mode 100644 index 0000000000000000000000000000000000000000..8d35b973b4be06a5560ce92d9abb4f6e393d5fc1 GIT binary patch literal 41022 zcmb4rWmuF^*DeZ3DWXV7illURNlQp~3`lo(3?PVrba!`2HwZ{~H>h-X*V%~g_nmXD z@5ecRV6JDL*|VR$*Iw&h_qx~k%X}1lhKz#@0|WEygP4#U3=AB2`6v|eG5FU^NnpLc#?TYFoJzrv;d% zrUmwa)?T?^(JSRDcV!=?ef*hkPmk^(hW$b8(6#bF^j+#>vN<2O1!!0VS90nP=Ut9L zj;Fo?@4G`uoBt)o?JG$7w&+K#?Y-v)=v6 zL0Gt5eSn6JuH*zePBJ<&QbA2E|BKUIhs>z`-;cz9Cznp9)~q`oFHrVDGd11Hl88Co z91lc$i-x$`pAZusE`Z3uz!1k_yINztl%GTu(LGTDfv)zw4inR}u;6t$ULP8gSy2xQ z3mYCDc6N3)W1U@GgdY59r^b}14AXs>VCn)Y9{Z1_1Baa%`~6?Zu#HD2T5Zf$P%tk|@FvaYlR*FJsvBoc&!fr~ra=;}xtlMoXliueNASH0TeQ=}1V-7d=V@-{+= z1o80k5m50|G&B+u6E$|`e*P5VOyYIFv=bHW{18F*Lg3}gmxx3kK76pL<))zM>h69= zLgLe+{j;;PfZK-T?+U}5tZr;vtPf{^g?^WFH9I{$JvD_UFjH+whlW_JSx*N3YBl!g zG0s+*tMAO6pF5G0ld~pBAsP>-`+9kKm1PhT5=!Tc&d<*y5lNDb_V-KSBf*$FjD1V` zic0d>ScA6OM0W-6MqEcnCxTp>fXxCgNMv+!a$cPp zWME)0J|HD6U0qeB_Z&T)^|J{>$vpf$E zj{stjNI|7$CH?(LfPvYcS;Ffs=k8|T5~!gwyS$_)o2Au%yASQr@3@YqAq z{;aM>hlajELzI+~iZbWO0v!fS8F1C6N%x%dDIns#!$|%k}^a z%4UlDNAa9^p-drwdgS=6%sJY|5ADy+&P_IPe_x-)LPK0)qGZk}q`~RMix)Z;7PswK zkoAoXeLX#mMwd$P{d|1TwSlC!Ve7M&&Eu1k&A`sV;c-1)Z~0UT=CAwd69JElP{weL zhJ}vK?-+K*qMJ+5{xPquu0eUrExQIU{JD=SHT&d<;1nxS{s7e{e%aYgFYsHmtG z$LAc&mP!x~#H_8WqcpsBTpuE$qB31< za_^64N#JrU4hfO&Z}mJADyX$)_`5o&n~glW@AnMaW?#RHfpL*|<|*gw%n{h0K3X81 zA0wXCW6xbs*{fG(BHXC2uiw+oU*IfXFP@5s{rA({17QtaF;6NO@~_@KdGe&PYI6IM zYFuMvO{&rDov7OfHJ$cf6DJgR<0fK%FY@PD zqr(wta@b*%RF%|*{=Me8OBq3ldM(SHkx<-s_x{1OIcZjtzt^M8dS#%mG>iZHup{_z z8ZYPZe|<$U0{4e#e!m`Q)W4yEXI?;Fjf^We{`V!?`_boLeBA0u15fbf-)v?3cytCa z>lAMbeExIgoaTKlZPBeN(!cp9UQNGD{{BguYM$~=QQ+US{1RlC8xsbi?kI^Koc_Gr zTUnt+FnoI85%k|I2K;Nux5vHBJBKqk(Mpe^>Br*d+R}6wj-f zGda=z>td@En&g&=_-OYbgp}m(0;N)klLWM#`Ha?Y8eNpUd~mo!7{iXfjq2Dl@$3Kl z#kuph^K&=VluyPVrpNrHUiHtlT%33h^uo=)EbJ5IUKp z%6Dxq{x`8~mRjQC$V5MX{?uFTivwQKb<~~~($ruy{1!yDni_U}2|YbMJ3BiqErNvp z)3Y;hni&Gy<=+2P)J8_Te@bR|(wFUFuC1Z8cu^YW*XIYK1+fgePlz&slk^D%jyO9r zvrwa!k(G5~d_1amC7}I}8r|Dh$k6lOekL^kQ)qf=rTAbssT+Y!RNGqyM&^a_hY%*F z^3}V+_VyKUHVFs_c4n)UdB@mTSnwJ3zGh@B{qTYf3=E8jh%jyJ0%vsHt}Y`(S6@O- zE?k55IWn@1jSVsqk`6ug!NCC~QVr$94hgy?WJDh>7ATiAIURgz^+D>10b^q! z!c7)yq9ej7muTrT#(_mGa#jL^fba<%k*a+<)Xj}M>7%2gBe@TKoFp3?n>`6k?%%!h zB-;&Wx6jLhEnlFJgFEqOP`S~eV<>cfrVSnrSaEFJw*76y_KBPAIUmTIbV|!2E zn2^WGdIuuU?# z5FrtfnW?ElNOLk;cv2Egm{>H8`suV`N^NZ|DG3Q8++%rp`4zQvux%&THG5xP{5>8< zJ*acy_j~_q&78Ddk^PhO+g9ak0fS9bT?0EUlPu4NCnu$qC zzIUPBddR*46t4jIfj}TD128^lVPcWV$qSXH%29qn|M`NN1Mf;f>T&Cp+*}HrAXbm- z^BzpHh>KxSLI7Y~0N9{Wt6+E9f0NKp!0X0Nv<$*+&c`IR(ogLOgl?&Lgfj8;Is&q? zVP)+jBO^mYR`YfCz@mYjqzk02k7Nb*wEa6}Qc7}PR7mn57nb-d%gc#8t`n1!lil5U zCRgF45@qG(8R_YBb8~9d7Ch!N%&2~Xh|ZVC#-Bc=^S{Z(qs{g2*0Y%UPDDmVMorx} zI?6;%U1+~GK^c;mEkmK;A$z}CM&HThnk0!V+sZOHL$kFnUwBARP5j~Q-?Wdmxk;%T zgBcU2jG}Zs8lTZ;RK*W;>Fw#6GIMC#z5V!LltXXc`c56K^iA{7a{RG_K*=#_eSW$$%xl?Ug-8uv`{xEd)hzqHZn#YKiGlGN|Ze{ORWc5 ziSyvhq^#Ai4|C~_lg4AGv)x_F)q{H%-Vw(Z&0GDh4iIq9w`_%fILgf4kzLtGr$yDM zW+H4PK4xVEDVI_65*e^I+%T{MB7&l*2GorA}#8SVJTWm#6NkCt=3+yQ_vt%NnVDP`x!Z z{@P?gvoCdjnp3Be|0Jv+Z)lV^RW{8oe(#T2{rs4*ltPBbYAMjbUi89p=ii|IIy&l50=V*F8zaQB)*Y4cuL3bx{Gi+Rt%NKB? z{#rHl`mBGy==vQRyfS7S0KHvinIw-_>wT?%yxh{_AEw%tXtU3Ov*8`Cn9Kr37@LG44@N zxh!jAw)eHyW5mOe194+ec|d^}*Aj9$bFGWeaW$Y&nu?oLky8HV%<9tx3Wjjbyu;i} z?X$%?{=ac^$jJd~F_coTy#F$H*%1u=HovoNQGI@h%1`j*m6z z7WScCa0WXr?Yi1x-~4{oGF)i_+-&47eo~>lL21ETy$$C z`E+r|DfsshQym~HN!UhXyMhi9C-bf1gXfWLiOw8;cIL9FxOBZIMqm~Q4>R7hFSx!y z8crgl5iPh9A)xl-VU-s#@HJF=m3O5TPkgO3{FE^AZUXkfE3ky4O+USDN-26QwXrQy zGml0*=-hw#tbax=A2;wZMr8MroCKMpz=b8xgO$i+RKlynfcHicQN=p zUFKQr-O-=+z<_{&d>U?oz0;l9ah#~1pAOn^KrW%+>?uhmAudiyL!&5PVUj0^0EUEt zCt^C6y(>qw(q=w=%yP-xfpj&^Yiv66eY*qOmF;jIx4#lg|0!pYABz4;Gss+j|Niao z?+-#77Oe(7)72HB+8eaK1U5QSQeQNXfiv2!_E|4=C~{G;v)9aI_4W19YSi!}`hWZO zEjW0fw^z4(`u6rV5R1;BR3-lz?t{Nm+kT}{BGbjoa1$C{uf7$RXcxN_Q@RjkJgvIu z8;(5|gi9AYomAF&Czof}9ZpK8)v!KUvIsB_N8+SCA|N9qKYUQFwbA!O#c#S;#ifnm z`t%dg-TkHq9Ul)bn1Dy^q6~mJX6A}}RECx&Q)9oS#K+e>U2fRf-o7(eYx~3Vk!HPv zqWN;{V>qxTsV>~A2Zz@x#r-5WX|fof$$NEj`j=m*&X(yt&i^En(je!oVbV>q*#oUM zpFIVM{mIs3iAHT1fERX`TOKDXy)jZ!QhrZg|5;yWxBB(t&!0cb%gg!XjB$OdigQiw z>SPg8iR@u$;gOLfF}Rven zX=FS|CRIv50a8gcgdl5l16afdDJjkG+K-Yx#`Lb>vRNohJQ>?O-WUbxC;;O!O_I{m zhV$eEOjO%B-qw! zq{*mk2`}4R+}>OPN`=bsV$^7y{M*LHMjp45)CKh3CWn}*d$}08x-zh4lQ#Kpvov1i zs?Wr;QqRf;)ew7KS}nnEF5FDc&eqS!-{lt;7RE|umzS5@Y(n=I8XdQ%!eV2m0HXnr zbbVbN-~v=R@Qr}s#?V0sIPHx312t-_h=_@G0g(_z1F{<=B&5e)KgPxsYNK-j=guXbXnK0O8eN&?A}`3JpwPP>OaL7JcVeT!4e8B; zx1%H`DtmqHj$70bH!`izDHj-g`SJI}`?m6`L%nu|&O@!Y7APW4*tWS;cZ!>nB@i{W znS8~9PrAC*mWvj~#?t_UK7h=dgHA#KGPzt&w+H#|`V7kbo)dmVtOYy=$YF%vh8-S* z#H%xeFa(Dspj%H#YqYnw_W@<*NT+Buxz+wdnZ%v1-heHE89#6OrOcLnBBo2159^!} z)}aNgl(@V~(?KJ*gn<|}98jDuYO0zw)*%UphANuRp&bxCQ;BkRRdiR$;A^|xh zC1p>4e-VI~quEmO7BBXJAp%m*i(lK!tkCUxP>)eGwrsBTsIU9(v773CohpD`SJ~h{Z`Nb*3+rl`*oaYc7dB_@4cyS3W+6 z8A>W}Mjm@9XfJiRPW*lDG+Rm(5Ju1duA~5YveLNAT~9Hze*xMUeJl&vd*CiSk%1eH zp;}If^$@dvW8a$1po?$dhf>@Tuq%fzakCuN<>4yFIULw?xJU-`*YW;v?T$u@UhA|m zCmR+!5uRg-PD(Z|yxAT~%ys?qG+#H*?`!6t7X%$-mug4u6Os?eW1%;P!_OejW9o~( zsQsIh(GQEx1t&hgRcCGVD=p=-j9=YR-|-dHV@j5dSpCB)yC8~-h8M1^e$GS9Bm!|o z`p@~(!5K0o1sVL_dd=H{K9!V(Y1x!b{tAM>LDcgJWpvD7zyE?UQ(RW1gh_D)_T~>G z*3Zo$oBRtwxR6)*tMgEMxPSLvGVYyg6H7xrKhH(Y{B7hTH1km^O0R^58vuRF2;y)} z{)>`;e~|1e?#kdt|J@sHj&Lv(RzkSTI5g3XqFS+alb$IDG%-u#9Uw8S1Q-c-q{gW~e6|C2}4f)5I;TvxY z+rOb46>h$GQt%@!PMz6GHZ@RMBB50Fwt(?rVK&JX!yB{K50$o%0G#@SE?M{SmmO|c zrKW%$m<-|XgU69~`DhPM=^}&H{?ua(IS_`ck!*_eX5v#U&1a^-X0T|0TI{Cfp~7E$ zvC>TW`96Tbq!KtF7LOuc{5%!}t`Y@@=*moRH(+!UOEZ--dh&FEmSFbLN^|}Lip}!w*hPZk8H|~SW4%Dil0mLBgo>5>8@K0eM%ai z@+|CkW_;nRV}%Y^UY8(?%^&eFnP2k(D=Q%KS(}qF_!EqmGKas3u+WvN`__XRBUeEo zQzheYp}KJ%OcpuUi1YyE_AMZ*_=F~NEt9`s^I?q@3TVE>jXpCxdM;;k)>sX%%NdJZ z8gEt$ulRsh8`^>n4GqP2@BZ2V4sOuU>8up6!cX$USt3o98t)xQ5qUR)4a6HzckTpO(i8g>0%YD@emu@6m{&_RqcCYp8fV zFXOAcV5?4FoPnf{+XIaV567fdk*U-N(Fwr5{;sa62Ir$FDrJBn($and8pPer?fx>Q1h7zA|D}P;hWp5JwU?+fBpKsqT>2uEyXlBAw2vgIy#fdSnkx+6hJYywQ~&( z+yY-^DCX+y3u0m>{n5$2y}d!~1+b&wuq*8T=)+uZiCmOJBnYnm+IPhdX{8KRpVBvH z-}7m)&uV_QME*`AU1+gl_DwDatDe)y|L7VrG*7;U^}PV`F;QfRk23x z+nC;{kbU^aUU?f^(!-^?q5@r@>{F10OGvo4y!_Lk9Ek`JR`m4rG7iT}OG_JepajH4 z4^&E-rPIm6eC4oJkgXdVqW@Hv7LY?89-r-U%ad_4~D{8Dj~~ zUvCW~jqbKUr@>y$?~17)i^f{kn*K1LHRTGyO!6gXCw zB_R?T@xT!F7GGxwlv@361(@s|gpzz;fp=rgid{Zi^5FRRTVUYP;h~LHZt~Rm<>l(l zXSBoRZeXty0B7oR5s|R4C~{a+@bG9(7pwh^s{E7s3a^U0_!`^D#7qrRj51S-I(oid z2hI^cV`saLW(7r%wap4pOC}>T00lUZJlIFpI+Njg<1<# z>TEs@R+>&?(rV-uA^^jSdC0P^&zC}6TwJuYw0zL$=;#Cm+rZrKS=Ecf6=dIZi5ObS z=LCN@WWBDIW7xL;vPy482LG$WDDg3|1JhtkrWO3;mi?!?)VD-MW|FQnQ#aN5KcIMg z9&TXu@j2~4H9D}Vsp+tY$#5VtCPq4iH>r0;3G)h^SY`$W*;=;m<>g1u`K|$du3V%B zs%MqPqhx}Df-60IxU6PI!|B2Rwu+G;qoA11Up9&tm1MwfZTTT zX*6{-HB%nMFi*1G_z60sxlhq-IrRiA9tQR-<9<=g8#epI96_C}^D%Kb( z2L*+kQB&su+KBFajFaf7C`GQbogGTAzohe!rto1IAWbwksdG_=cdc_XgTRSQ)*Eed@*$9*$Qb( zhu%7Bq&$^K*JGN_21D%n2b{Zy!99mZ@$hK)`VZV23R(2#Lid4qjOmwzgB@(%02|Rq$8Q| zNCjJHR<&_vcP+Z)Tv|a8n%xTV|D+<<=y5H~oY6-LN zB9mIIs~OFWyE8f8;`=@r4N~Aya4_S>vhI*Kn1h;Wwt1%cKJrB}1J6RKAbswGa>eMf zIFg4$Is7nb1u-A?(TLhy{Ka?NNxKp;qHIn~eBZyJC%(qUmpPKKvfag-K`Lca#dBNy zRUh;2TARg-$VT<|^r-xg{$iJ*pS~DB?Z+IE=}n~V-|%PJl#$EQk9OJxo;Z0mY!_Td zD2QeT{`KGd@~2>9C9K@b?>E+~IZ5(GWtBm#I%AyBUhYj{Tk9->4*>63whwnlkdg<0 zZur9$2a3&e^62JNPTKhg5l(y|jd08~vjJjf%qIUY z%R!XP=Vk50Jd%{^Go{;SGjJh&Gs#-AV#Y>w@PBufS!x5rSLedeVM;Qc6V2(K7F+Ju zxpBn((jt{mJGMK8PQDI^C4z;49B0oqBCe^|5=LTC(vMfG45dO6FcXO~e zt3n;KS;oC!WTWpy$S?-im3nuZQTi?&M;zn&WIAt%*HLBY@uLjCO>9>otZA1Tl{{t=aArEi_r>F3X|g+ zyBEMJ_?I%3U%}~_PwV1dhv_5?+CF!5t28Mw|6bySJ8lhVr!mE}20M?BeINX$pK@=F zVgIe5^er6I2Cn0m-({-LIPM(pzy@)#Sr|#RfaqjmKG@q^F#Qz{<~Bn{i<91V^pC-9 zMiAEvfzv5T4*A13^sZ~{5wsu`*c#m@d;$Yg@gsdMBSVVQe%SJ({hR$6$i;{sclW+S zhb2eHU-B2P`CE$SYfQ~&8n_H~*>B#yP2WYx;6u23Nk#d^&>UB9nE_y3{Ut!>UePZ0 ztrBiWo&q}+GjzuizgH|d`BCXh(?)0W8Z`kc{srMehigeHh`F0sZn2rtWzyGEa`HZs zL+HTuLrKlX=W-aEx!oKu*lW5fRs+lHB=!E4ypt|C;zO7XNQTBp7$RlX^qs27vFj&7A4)FQLmh9SvrDymtC3{~--9{!d#maCvxbI13 z6-BG;h^C`Ag3rOWLff;ya+|5r^qJD#6&YJIL8aZVw6%PysF6ut`h&V**V<75>*2*U zsAVI;c{?U2$H!l;r9hR;!~P4WBSyb@U3!A^s8|jIn`uzEG z2p&heTO2TSz3%WdK|hbnjhsi19)SwCCC3?12}O`eU0xpzQmMZsCI(b2qv-@SV1mAX zr}tlOoY1YPtki69Ds60Z9~Y5)^w>tjZmqM`wwa5DA)|cD#*IFwK1F8>Wcl*xt}m+O z)jzrG!^q@y%4t328$TbIhe!hT{6S zA7Or_*?isj;GpH{_B0UTa5?Vc(QEUAYW(8sj#Ayuh2v;Y=LKx$<>^gzD>{;AalP#A zc0?lV8vZI5L*JH1GNSU-VK&kCU<}EmCqcCrI8=O$W)FFrf;0EnDGpCEx8ti9i{(7FH{Ym8xcE56Pm93Ts4aSx<3~c zlX<=%z}xZ&TD#H^2@WJ531b;u?fHbp+Lxr=Zks7%CTF3ED!4WMQ>M zM?^%_gPA5naW5f*h|c~e2@ws#QQVmW96rdy0MG-HH)S#yU}<)o01PLgwdyg>&&~Z= zT3YK*I2#aSHJg$-Spnp)bTW6X$v7qF&E8z^&!3r_^JMBK>9~{ufCW8FgP?6h$OWwEQ;}7qxlPs4@8NP zfShy}(7YhSY-?+ynjIY-ef{)F;EBrxF{Q zgEObamFt+e=Po4O@f{)D+FY8#)_I;~2#afkKvSVSUi}yrcCt{F2@t~S>a!q+6Zl$S zdg9*iu(zP8qEZ8ZvdIp!+KKA8i_5c%M)D|e(wE(!d~&RrK zX}~L{IBC{fR=?74T<^_ZqP%GF9U0GXL|9Sz35>bI*+*y=G6xc8>`lKYrn$Km-=z(+ z{x*&-6C3ARk0FGHmiJe}X&X`fYrOw6oa?kSipahs6a~avQA+sg3u=RUwIyIGpC)`MaiQ2q44Uy;82u+npbhicdp`L4aa z9oXaPeL`rJ>r$*jy$f_GhZ_3x-v!dpkbP}x=O!o5Kw%K5U4cEm4Fidx`}zKFd;grx z>_^NvMW7<(esx-Oa&WhZK=DeYN3*)Hi|@vCqpdd>l56S6mvd$D>(65C=- zR?6`ajFe3I=%U|zeuz#lr^6-H&27G$p3p=RJgo!SI6^%s=ydhlv_9 z3uV|znu(=SVG1+>+L#-TD6E6;7s%UJ1;!+dzVcInKSA!)uW(v(QOxenzxu@%^{DZ9 zt0HgzN@m~|+Kb4qcyG7b+S*oD`=BxzMFY+rAK*xUI{^nZJtG5XK#_<*Jm9k590SD3 zx1c*FN=BVlA5h@|Mb4Emd_F!tT-*eplLl(F10bvd>LDPzY0xNF!hSSFQy*xFheme2 zTOm{gWmjeHaKvo$qx5_1=4?__R8y`I7ofX02kJ!67Y~+VAX^Ikmk9VV3qh-oLQ(yDyGuWn~3$N_(@_C1v22|JDOV!zi@tF|wIWp#fG3 z6B7uhUt?lU)Y)q~3gzYI)_B}p0-kPeejec|wv6*)JpJQTK@S>t_8=_heq-wlnsAxy zP}+VjYk}iV+Jtw6jh$+OF#H^fJR2p7T{^r(dV?wn)p)kcO-9BV3xp|Lw#OSjq1dt` zh8%uPrRPpn#yhZcsVLDaolTLe_+y!^GPz^q+`1kmK*5^l!MLCA4H<~$ZrHAoNy+X& zm_EUIl=}hboF^9xMaUw?OLMCW3%~UjcOH!b-lRW~L;ejK1!TmiB82yHy?(E$;6(cy z%zA*H^^AhnPmm1+)e7Mxbj)e9IOJMip=mAi6NlI<>3*Zvd!EXpaGTkz)JgEQn1`;u z{ue%aO=ksL(|IBDF~!}G5SM^hs`VP}Wq+r>EnU=YZLa|%C=U+-fKJd7gg|=-{JFb^rSx?yAHc`x?*=s;ZO?GZ3O+Tc#oZKQ2ffK<(Pp?vCrYb4< zt)QSFDQOukxm5m41rYb}_}dN0vf*hLs$tDn=KY4K!SIn4y_W~zv==DI|H$n?%+ z<)kbph!7z7dEIw(8dl~SryuLr0K9W`CDR}MD@g2p=(yLxZPxIL^nF=im(Z@6??;eh!+`K)7LfQg5wkdt43(4-!2MwA_l68eu*u@h%TrjyY9nK?= z2=CBe*b9BT+4GuDH*FdoeRgmPcP}yDnAe<2uA&w|j94$K#2fdUx6zw8z39KBGg-ac zWqus*1F|^|yU%FBbmLz9G(Rx{a+m+irm$E6S&jB+Y$EAq>lI{U{?%xm4t&EnP{^a!`x>_99%ETX8Niw4?y7zr zzG($xr$)@TQ8K=oAna{3&33b8ouQ=Em%>|xha08%_NLm?{Cn7%`C7>zae}8kbFswe z`Q$1?Z%QbUTagF0zsCvLzmcgYJ`-LDz8o;vbRRLyF~l+D<6VjDpQbR(dk zz6J!m3~cua1sNa^`-AfMGaQAhU}-1h*~o7HPcJQ{QL@Kzdc#1aDjuXOsXND@%+tnL z{U!GFI;Fi+a(fGDNF~>01XA#-NpIZpqI#8c(lKbidm&L`uxAx-&>!)-+Gq-t(}oWM z$Kee*8q3_E>zLO?){2@ccJhqrXWZTT#YZ!NF-7hrvACO>p zy}eb^%xklZcrPd45D^i9ML<11IvSlUardGwd^{HE9*=vY(i&(Ro8y(VjTdJ{#<%Iq z3{BA#bERHJNM$wAY|LpPVHBQ}GA?(G^>tt}|FMxJC%hJ93^_Gtovy#+^qlNTmi)65 z*Sc}Q7Z`Fv-!uOT|K-AM(=W5|UgfDXWhS11Ft?xmZnb@i?(i_xKp%5`8xZIXL`^%;lqy@jpz? z&{gq|PiEQ9k)J!gY~DXMEl*NYSB9ulOOwU7&Qd8v5>_Pbb{T!^3>`8;UdbY$QwB1b z&+br`iEH^mVz;0_8sCEH%ja-O3M5Z%J*ojN*P^BT0U5KYxnl2gJAXK+h$fX zvVaF|-M)NFEk5h}SH(~mO03op<_z58Y8=6PV>(0zk?m;tf_9S@CO2D>XeX)3;oFC` z_Wo-{`~K`CnA9h!NOfwbdL9;iq@D2bi&x}6?2PeAj1Q)Z)7`Soz8vMzuX=Qgi4)$rE^x4 zVaQ}*X@m-hVco3a7C!bfu=wyJo&S!4ii*o=Ut2*TQjBC1^bn0#gB7!T)!XZubI0yA zs_2XUi{c@EDS}vh1kU~bvk-M(+hIljW<-e!Gq#h=uB*M;aYcJqK2qSGi@@9hY}&x= zG9ZNF7Qd4FZaHd%kB-B=#if}Pts%5%gIA-FvKmK}n_T)whi85L<{%gWYW{rtK4g5($ zg0S&c?^@np<|u73RGCM%&mr$_m^x@HJ~w4>w!+wlzVHQ^Jcoh%XO8*2VEBbx$$Trd zo9o>*+TiS0gS4_}jAUGif`)sgafiH`2=7(A|gkCy^Y4X)dZA_*8Ekk>fT?~SGbKugnmFp5nZ@(s3Uopk3_K%$# z?N1aO{(hnQ;V3C-;RNQemf2E+b<*FsY1W_&lwRG5TCQ9bWrr>lRT_@u01p1`Vj*!? zR&vp4PC*5eOO}y7L~j0U(%?`#+EotqGbHEXKHPAj|>ef?lZ#X!v@Didt1! zS>yon%A!sT4Rwgq@9d+OD0SJ_V|f~G7f0G!LI8J5O?aJ~K2sB)~2P}O>higFhq-Cu!_C>MrN*zlr21els=>2Hat4PSm7_q;Bj)cJ!zLd6$vluY5 z9f!K*1@i09Y>8NG7ra7o0+(kHQ`wP^3Ei;IW@YhF`IFYoa+>p<$!#xM17hFQi`2w; zhrX|8 z)EDD6RdBk;%gE)OU)%)E6jtMyezWK1Cr$&lR!g466ub|dzj22}CaQ*of8pq$u`8_k zW&Naa1-*n4GlOe2Z&2wV>rb4mCnn+CwOv3>KZ>hsY{oI4wH*^WPJf)J z6b|zZIHa=)(V%v2m}ISC1G+ma7(~t+%Ww$g-uGMmWp*w;HU}WK=z-{?XOu_q*_N^Z zhQyUa)(0bT`dtly4%7JH_sUCHA5NrI(b&Os~I#;%1 zVNpGp-pD3vFco8H1DX?dRhGb}+u&~ek>z65?RJl@cVN(}@ao(0mcI07r+RW5Hzn1l z`r55X;~*%ZuI#uDx%mw!{;LEr4k^f*N4>xE1wrO@T;|isti|YyOit$h1 zF<@sJD}y{56OI$uyf9f|-9mzWIxB~d($EH*OrkBldXyEf2n?+t;Ck11QFP|xP42*5 zUIpzV(0l>}zzJ-Y9*4_FL_dH0NCkRRK>TUe*`*EV-yN+3@uzn>dm53R5CfU3sBL!p zlLCIAn!!%!z%!ofPS#rEO_5X(ZQ zslNatU?*U+6>*hxU(pImK~_5BN2UY6ABM1=p9*;FVSXX3G8!}Xc&07FwmopGt#LX) zB(k$rQ!Zm;!$|aVc2@mnIPVFdENyJWrKB(d+d&;SN<0f#EeA-pH%4>dAHPpvHCHKw zc8)TaYQQdhvKV;ppYE+b=xio6C8k@VWp*ZQbL!|^>5^iO=x_!Xh4$_zxsc<)R{~Jd z2FcJt)%z*oWtzpLmn|11W8_+gVq{WR)@3Q`9_zM{5oVbj}qgEJ(NMR$mW{W3dAG%g0c}NUK6&h zRQh2)+cu4I51`ZfL;-^%BJ!#^ig!lYjjDM0Jt(NY=)fP|u=E_qLiwh0Jm{Jri;wtv zY{p7MG~yjY14ICClZB^6V)5d6%u4zB707h_OP`y!SI9y_Wum@O80=?Es0DZI?x_)h z4oO3lD*POef-M=NaQ{JA>(%wb{iEO%ww>ZJ&|fr=%;O4Fo4hHY$Mxp=8V?UIpTH9b zrl=GWyKleHy_gg;wGk(WANZxGkRpCyQTYv(e?`(v-TQhE&w)CsfhlFTtx)>muwm-2 zOH2cqC`vwNu|JOwJ$Y22Zo7pY>EX+ zps8GXy7BDr5Qo()HYJ5HLDM}rTIAaMi({S2rIYOLFyNO`&+af$pO!=LSFPbET@A0# zLHTyD|Jq2XJTTnTBo0FuR1&WO(cun9{P9u(gHh;ruG}NVUcE(DW|wwX)KlpZ`ZEA#{6r`8fMC z!{b!=T?s}6y=ylhcqDj2D1#s9;6&7qvMM@+=>I(_-~v}h-e15?(#a5VP^5QI@S;O2 zf@7XfuUe?={8k`jB~oJLs>68he6~9>d{BC2?qaTeES2chTrqE$BN+2+mtV^b|;QI^R!gYVrWc zrJ%8W?I?v0x&V6Stxm|gpq({=LbDF*Ct6%zI336zUM@;Yz-+I&r);?D>&=}X^U$3a4m;fCPZ9phA>N)^%f zh1jYcXTMFUBY^>xAN49T&^<}y!_Lm`b~3IEgleQdpxMxDq2U75zd$Pi&`JZX2%tF? zrQLw+1i+F4O+e#s19i1eXjfO)`uaLhi?Jr`?Cmu>9sJ2hIdA9&FqxZ&r+@_zz-p@F zWv@{@`k3SSwOA=^N4*UQ)6O6eW`P9DRmXngVbJ*n%fx1&@CqaxG8(6(@GPi$v~PirXgMc@=B_8kqMp=fA4BA|V}PTU z3x6y z5(o7JBYk~)K;eMqPS7XL<9bT52%1|iZ_XA$XDU8utT#5k00lHo+Ed>PyD=Vlu)!X|Uf=*uzTF|dR30YlUrUpuUz@=6ad`n^O z2l5c0N@o4>ecp|UFJE_9sVKIJEh+k#!E7Z7v#NwWdDAMj^gyqj4$PRhU{EV%Mq9S1ei}d5lG4`i153|!?X_LG`4zOY{Y$veR!rw5>HU`Y)*U%Km0W7d7;SD(@F~Yvb%(U4o2WWLh47aaOB- zuK(p)OixAeb+rpBZ7%YQn%w=A3xfS`Q#tWuP&i~0 z>D-y)swaP6{Q2DO-;ziWM>DZo8aEO$9}t{ETYy ze6V=LdHZE2Tr(O%>CXMe7FJrRFn!%l7UT;b2E42i-U#AE&33$R^yx&&wr9y=@rM-H z2?SOpSmzyE*ZeZ+cLaUjwTfddz6j3=p5n1vEq8_4ak_)2>B+kWt=dvzV!+;W=VVn# zUN*k_0-PQeCg#W3eHYb{<72xgpJh2VQrdoJ=HyJSlvBJ400L6bfw0mG{->w24L~Qp zK&I*pC5BfZ;=7$QgB_@mls`#abTHdK&J3z1V{v&^7qr;k^226{m0_;o+d3bt0J#ts z4to|W>p?rcE6I#3B)s ztQ_SWwLnl3DN!x&n3!M%MR@S58$g5`$Dk_$oN5t51~kw54FFID3X6{V6M>*My&AL%hatv z*N%21vCHdC<&{1AGx3x4CJkLh*bF~AuPj%Ns^om4ZIvn)6>gL0viN&Rg2TOXopYXhuYd47@XBWIwO7nJ<``p+HOAB-6K8g9htHaF zmv~row6xstr$RT?)upprynaW?$awrc5*wRF@#RnerC6~2ecbwv1jv|xHUQ|Fji9`_ zFw3If`US8gOb2^};r~l)l$`k4TOcPQLdrl$0?2p3fdM9uT?pR%-$z`>1N@xFwq_B9N7nMz)N$l#jeDmp$weA8@v^J`gade+>bN@pGY1*$fn*62fd29X72 z2V)@P=9R3`Lp`|>4)h@$fy%^&6u{UWWVivjt1RM7PXXvWOV;dfSfe+g)Aisk>iKdm zZqK*VIjju3?tT7#acxpamoM=f-E;<}i!D$!!o%AKHKq;a2ct4j@zc@PzI}jnJcZTN zu>5b%XqT(=#z`CzFT3Mv>liT<5789hjqFdPs~z=$QW;sG>>ZF7Ni8WJeNM)hJN@7} z?mO9|ljU3aG_K)mUwUcLQou*<9RV>r73$4sUqc95XOs-8#Y%ueisCseBM%{S{H7@( z?!u;D_BpDsYPr2C@W@bu2p%sF@h`n;cI(nqyNgKBCyWuYFP|z;%g+lnRiLpQhaP;r z{YECcMj2?fJ8yDsh~5_{rV0qtjtNk8@0-oo3yhw&{&Q!*-wDO~D*s+69h*Nvwux&5 zI~a4V=<@MUr?k~q(&k;T%Xi&u&zv>|=VH6f3}VH6Ru?z_N9I=fKAm`)y8>cF>mdF? z_HOZ|o?@3cEzT@0MPfFn^Oj#ozluGDJhYEZ3S}* zT*au{Pp3-L*VlKE=6O7oC9rd5V4x77J7D^~74WVN9q6G+eFr^_M^tRBJac+9(#QIy zepK6R!iz7=uq;3zxIuWNeU{Iab>ekRZqA(ZmPAV;eFJgr_w(l80Ti!hTqM1pM{C7% zcdnf*-Zxg^EC+M0RU20B?bFk8 zVUBJ?JP{EY_-=_*>e<8*cd^drkRL;$-g#15U#Le{*we~^5USJeZlkLD6FBJr+aR#o z2uMf_eNO?(SFHLciB9eQ^3V3prK9!?(6<8HgD|mtYG`R`X<$f1DnIvu45QiccSb@& zJ+Lc5Bj&5rZK?pM;1yB;(4mn!p`f7+ZJz`73|QR+fI-sJHz3=4pC`+7G`Ng!e$w{c zgmt+tjXb!*hl`s^@18yfcV@oeFFWz{+L*cbSvwC*C>Jy6Qh%dU zbM}AV^-V?@{~`|drK*3flOSkFBZT9m9$r}{LwdLGjo;goY zjj+(D%5-YndQXR=kCuwzAAi^6e4>vu+FWkCUVlfY)ZL0z8?H_dWErGMhZX7-%QHNu zYV^rhjE8_Ll9G~g6)(Wg|F9jasHiAU4ZzPARp07rrza5pAE=R4^lsrJ^lhS-IyaiJ z+0YDnS-B<-B|d5MsQa-Fs4ghSu6{mh9DidzLrNt7UVfl7{$ubZeE|?J8rr$Nddp9DYKX z>K!~Cp}!B2m*1PXY~hGo6AmGp`F)(hi?LUF(fCliEz`Y-|F7kOC3gQ9@7vp+>)P|p z^6$uFaVN!!L2EsIk&|yE?=rrhjpM=rT0*fR7VJZkFIY$3-nT}9PsPf#fDndFM3nXA zOP=a{*9cqZlX7!C`BiWwZ8`}OY1n;GNQ01M{Qc7AL-3%UmSz!`ql5Fb@{gS?iQUqS z9z&jJ-_}@yli-yLWM;SFM87IFgzHVa*UkEP(r*q2pJHq?>bnZd%i{-WAkRDOg0Zt1 zB9o@flqO7_uQh)s&f5`_s-02XSo4f7V(+!Ha<)mZ2aC`DkRA^dw)~uI2_lg1w&}x!QqyflbeWX9(?fpM%A=F2O3SLLq{?UP9=*%JmcXsCX1 z^7&|TrZsQHT)bHON9)_!nf7j)Nqs*vE%sUTU@qjOPe_a=^NLdOGpK7U2g1OrR8^-N z`dH(=N7>Wk;7Mk`ii}83^q01N1tTO=rOA*i3xpNh=k@jVoXE)1lvo1YfB%etn;kaL z+2on(#-8HP?IXhUVGP;Hozzd#ysE{s+m`!C07jPUeTaT)XPRop#-r_;V_y^ILblG| zBlMrtJ;Yn+q~Zw1o4kg0Q97SmX0D<3uIg#9U7~;3wBd^kpq@Hsv-MN~U2%v8H`QIc z_v@#Yf=79;@f6nirgwbqT62q`(sdz!*e+As+CBm$#f!Qb2xe?-c#V65P~UQyTUi0r zsblJC(-#jOKI)=3f!JlvrJHrTXw7Ya-_Y~PB`G$Q)b2!aQ=@T!O`2tA)nd0)(2ymk zPjGH5=DyeA4rh^+CgIWE_;b%@>t(_H$kUjxB;g%WG_0A;=nef3!K#J9&~40P+u=OH zxpT8$bqoxu3McJb#5$XzDtox4EI+EyE|u*|rfIbx@Bd>=0P>pZYD2*Lw0xc>4Y=)_ zoo$%PHX)oY8Pd}5V$kOw?>9CTGo&-3Y?-ROw8=bC;AvW(Waw@Xri^R*#P zfslBv{pYao*gO&SwXmKib78X85{BivAcS$IgfQy5TqbKvTX?9ty1FaF}e| zjD+(nf0RytMh_w!tH$m8x0@xL!lFqo1s8u?_Nu|AO;Ob)t&Q9m3S-3ZPO0&kUf2t5 z7RHG_SI_o6J6ZLwp0>fL=Quw9mmAJM7Nl_KTu6}Gh`sS!wS)iB;;ew{roz$b2+0w_ zqqm@MnXk!(tY*n`E~g%p;ve7y`LMU`iOd`_;dNZo+JDL z>w|S&djDlAR<@7|cQmfo@09Kex}!c_h@?vO?7oEQ=88}~v zB)V>6q&=>_Y)ydA>PJ54U~->ODG_G|qKF*N^tKo;A4dLavEsx8Q9-TzieW2Jo0SzpPg_`NgMWW&{ZTi56k!K+`VTB>9o zPnF}GBNVrDq}RHa*82C#2Sqo8wn1O#{cttua&VC3a`wB=7oqjs6mUo)YCQB)D`P$G z3l_~x3s@)GK?~lQ+#TD^bIHb#lS~||ZzKi8irD3%Ni^TRq-wp|dJNsW?g!Z;g0OB- zS7^;T(mKFqJ6*rdtd+LS_Rn{zkQkBgzJ)UGhCG&k!Y0I|p2OZZ*DJl_lNWuIuXIJq z(KAveq7v0}x|cv4A4x>oG!CU%!eoN%?%~?sX%9BBFHi@o???aXtPWTiSoY1a4GqJD zu1k}o%g1M_r4XqQvZ%_;#7Rdu${9(zp!4=*wJ11K^&YN-?Aay$QG2D=#JyEFUc#73 z728JjnN;qZ5pTMSF7I@mZnbXelvBw5q4nR4{buos6fY+Ie{s*g zbOu7;sMWRCW}ZQO=QIz{UC~^Ged=cW-$*T_jh+wjKfm0`C)N4Up25VHzRfxoPVTv>xgYmfRf z`^D~R9oL#nW4RFSn)+%FKGh)nO>Mz3>35oUbv=2)D}mpLQ@N4!5% zX1vL;VRb?69 zaPN>*H~qj(TuQ2C;A4oxv5Iseaw*Zvtx3VBtt4;LZo*rII43_O=F3>;wpk&dBbq2! zgUhu0v@}q^3-+LCG&oK}`x4flc#4p>On`YR7Dp?Ua(rkxvxWC?Iui%aNvBu2D|GlN zHc6#W_F21^pIMDzI&t3%d?X6 z-6uu%2&ag^Dq{Ifb^7MtSe#278UKkGKz{Kds>KOc-?n~#K1yGwZ*%OJNwIL|IwJD$)vhPqsTn>hEhtu^q-dix`yHQTyw``l(e0CaV?lyt zQI6@sGs+#^Hj*tO$xj=U@%haZrPIpaNfg&#k$=-Rza%+Zt)GNFJEj;`D{#{tBHAgj zAh%$6>ib;H!Zt?6X~W_t6^p2=$zgM;Nb;IiO@k+2`-`$USIz1c$rkD+u-sDlc-cUx zh~@tpms*I!#60Ut<-TzcE%X(LdQJOreTZm>U$;a6YQ6S%6T|u4xOj%Oq0nt z?hN`5;^-yUi4tHy3DF*%)dQQqbqwo)Yd|8^_#eNzv zh3`8wzmJ#|D?E03!*wjPS+jWbYkRJaMXBTbgk!+wRPFAx|1fxGFvkkg6CusOskB!}(4aH8DsZ<@^_aFTS z@1VaoJFPBe0pqi7k0FuDW=kgwkfW-ag zofJw)M*)*P)obXvVfVj%VR50Eo15ebCJz*_V}Y@xpvs;|;mc9Xxv7+zK52phe=7|) zDxBJOYkIYvQ;Ev5YoT!685R|#fn(#a#)!Oq{BFC_i6fwUR2#!)Wq?G^z8*j5SuY?z z%P}-}^Oez^k#X=a5y5`vPR&I`Rz^vnC;ikOODgZWUel54*-G1CPo_}N4>=5r*U1UI zo=;%u_$9BvtA@$`Zr}y$n3oo2Ax`U}-ei#fT7|})&Fcf$T}tu2ACQ@ak1>iKaH=;R@+U`JCiiQ50DyZlE#*>l-ca~?&Vy=*i5b*K zcT<_a`rHg+YIr^-7!xoL!GcVBVfEV~m4b^Zk_V##1{PyuxgUprbWHV$o@WL}Prgz7 z7j|K^9ij5=%8N5Ue}iuDxzgj`@hkbJlVDHAUZ|b%Hf){;CN|3lYwR@dS4=|nFe*

p8eZB&PFB_efywDx?;o-BK!{$3{b(c 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 literal 0 HcmV?d00001 diff --git a/annotation_series.go b/annotation_series.go index 01d8569..6ec28e4 100644 --- a/annotation_series.go +++ b/annotation_series.go @@ -1,6 +1,9 @@ package chart -import "math" +import ( + "fmt" + "math" +) // AnnotationSeries is a series of labels on the chart. type AnnotationSeries struct { @@ -73,3 +76,11 @@ func (as AnnotationSeries) Render(r Renderer, canvasBox Box, xrange, yrange Rang } } } + +// Validate validates the series. +func (as AnnotationSeries) Validate() error { + if len(as.Annotations) == 0 { + return fmt.Errorf("annotation series requires annotations to be set and not empty") + } + return nil +} diff --git a/bollinger_band_series.go b/bollinger_band_series.go index f74b489..902b541 100644 --- a/bollinger_band_series.go +++ b/bollinger_band_series.go @@ -1,6 +1,9 @@ package chart -import "math" +import ( + "fmt" + "math" +) // BollingerBandsSeries draws bollinger bands for an inner series. // Bollinger bands are defined by two lines, one at SMA+k*stddev, one at SMA-k*stdev. @@ -147,3 +150,11 @@ func (bbs BollingerBandsSeries) getVariance(valueBuffer *RingBuffer) float64 { func (bbs BollingerBandsSeries) getStdDev(valueBuffer *RingBuffer) float64 { return math.Pow(bbs.getVariance(valueBuffer), 0.5) } + +// Validate validates the series. +func (bbs BollingerBandsSeries) Validate() error { + if bbs.InnerSeries == nil { + return fmt.Errorf("bollinger bands series requires InnerSeries to be set") + } + return nil +} diff --git a/chart.go b/chart.go index 236d714..7dee166 100644 --- a/chart.go +++ b/chart.go @@ -132,6 +132,17 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error { return r.Save(w) } +func (c Chart) validateSeries() error { + var err error + for _, s := range c.Series { + err = s.Validate() + if err != nil { + return err + } + } + return nil +} + func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) { var minx, maxx float64 = math.MaxFloat64, -math.MaxFloat64 var miny, maxy float64 = math.MaxFloat64, -math.MaxFloat64 diff --git a/concat_series.go b/concat_series.go index 2c2098f..46d8dda 100644 --- a/concat_series.go +++ b/concat_series.go @@ -30,3 +30,15 @@ func (cs ConcatSeries) GetValue(index int) (x, y float64) { } return } + +// Validate validates the series. +func (cs ConcatSeries) Validate() error { + var err error + for _, s := range cs { + err = s.Validate() + if err != nil { + return err + } + } + return nil +} diff --git a/continuous_series.go b/continuous_series.go index f182eb1..d6252f3 100644 --- a/continuous_series.go +++ b/continuous_series.go @@ -1,5 +1,7 @@ package chart +import "fmt" + // ContinuousSeries represents a line on a chart. type ContinuousSeries struct { Name string @@ -64,3 +66,15 @@ func (cs ContinuousSeries) Render(r Renderer, canvasBox Box, xrange, yrange Rang style := cs.Style.InheritFrom(defaults) Draw.LineSeries(r, canvasBox, xrange, yrange, style, cs) } + +// Validate validates the series. +func (cs ContinuousSeries) Validate() error { + if len(cs.XValues) == 0 { + return fmt.Errorf("continuous series must have xvalues set") + } + + if len(cs.YValues) == 0 { + return fmt.Errorf("continuous series must have yvalues set") + } + return nil +} diff --git a/continuous_series_test.go b/continuous_series_test.go index e8df2d6..c5d950c 100644 --- a/continuous_series_test.go +++ b/continuous_series_test.go @@ -47,3 +47,26 @@ func TestContinuousSeriesValueFormatter(t *testing.T) { assert.Equal("0.100000 foo", xf(0.1)) assert.Equal("0.100000 bar", yf(0.1)) } + +func TestContinuousSeriesValidate(t *testing.T) { + assert := assert.New(t) + + cs := ContinuousSeries{ + Name: "Test Series", + XValues: Sequence.Float64(1.0, 10.0), + YValues: Sequence.Float64(1.0, 10.0), + } + assert.Nil(cs.Validate()) + + cs = ContinuousSeries{ + Name: "Test Series", + XValues: Sequence.Float64(1.0, 10.0), + } + assert.NotNil(cs.Validate()) + + cs = ContinuousSeries{ + Name: "Test Series", + YValues: Sequence.Float64(1.0, 10.0), + } + assert.NotNil(cs.Validate()) +} diff --git a/ema_series.go b/ema_series.go index affadc1..44406aa 100644 --- a/ema_series.go +++ b/ema_series.go @@ -1,5 +1,7 @@ package chart +import "fmt" + const ( // DefaultEMAPeriod is the default EMA period used in the sigma calculation. DefaultEMAPeriod = 12 @@ -99,3 +101,11 @@ func (ema *EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, de style := ema.Style.InheritFrom(defaults) Draw.LineSeries(r, canvasBox, xrange, yrange, style, ema) } + +// Validate validates the series. +func (ema *EMASeries) Validate() error { + if ema.InnerSeries == nil { + return fmt.Errorf("ema series requires InnerSeries to be set") + } + return nil +} diff --git a/histogram_series.go b/histogram_series.go index 0542c1a..153837b 100644 --- a/histogram_series.go +++ b/histogram_series.go @@ -1,5 +1,7 @@ package chart +import "fmt" + // HistogramSeries is a special type of series that draws as a histogram. // Some peculiarities; it will always be lower bounded at 0 (at the very least). // This may alter ranges a bit and generally you want to put a histogram series on it's own y-axis. @@ -55,3 +57,11 @@ func (hs HistogramSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range style := hs.Style.InheritFrom(defaults) Draw.HistogramSeries(r, canvasBox, xrange, yrange, style, hs) } + +// Validate validates the series. +func (hs HistogramSeries) Validate() error { + if hs.InnerSeries == nil { + return fmt.Errorf("histogram series requires InnerSeries to be set") + } + return nil +} diff --git a/image_writer.go b/image_writer.go index e9cfd7a..85ce0c0 100644 --- a/image_writer.go +++ b/image_writer.go @@ -38,5 +38,5 @@ func (ir *ImageWriter) Image() (image.Image, error) { if ir.contents != nil && ir.contents.Len() > 0 { return png.Decode(ir.contents) } - return nil, errors.New("No valid sources for image data, cannot continue.") + return nil, errors.New("no valid sources for image data, cannot continue") } diff --git a/linear_regression_series.go b/linear_regression_series.go index a33a0b1..bcd045b 100644 --- a/linear_regression_series.go +++ b/linear_regression_series.go @@ -1,5 +1,7 @@ package chart +import "fmt" + // LinearRegressionSeries is a series that plots the n-nearest neighbors // linear regression for the values. type LinearRegressionSeries struct { @@ -130,3 +132,11 @@ func (lrs *LinearRegressionSeries) Render(r Renderer, canvasBox Box, xrange, yra style := lrs.Style.InheritFrom(defaults) Draw.LineSeries(r, canvasBox, xrange, yrange, style, lrs) } + +// Validate validates the series. +func (lrs *LinearRegressionSeries) Validate() error { + if lrs.InnerSeries == nil { + return fmt.Errorf("linear regression series requires InnerSeries to be set") + } + return nil +} diff --git a/macd_series.go b/macd_series.go index b3b80c0..aa3b683 100644 --- a/macd_series.go +++ b/macd_series.go @@ -1,5 +1,7 @@ package chart +import "fmt" + const ( // DefaultMACDPeriodPrimary is the long window. DefaultMACDPeriodPrimary = 26 @@ -287,3 +289,11 @@ func (macdl *MACDLineSeries) Render(r Renderer, canvasBox Box, xrange, yrange Ra style := macdl.Style.InheritFrom(defaults) Draw.LineSeries(r, canvasBox, xrange, yrange, style, macdl) } + +// Validate validates the series. +func (macdl *MACDLineSeries) Validate() error { + if macdl.InnerSeries == nil { + return fmt.Errorf("macd line series requires InnerSeries to be set") + } + return nil +} diff --git a/min_max_series.go b/min_max_series.go index 4900c6d..db153aa 100644 --- a/min_max_series.go +++ b/min_max_series.go @@ -1,6 +1,9 @@ package chart -import "math" +import ( + "fmt" + "math" +) // MinSeries draws a horizontal line at the minimum value of the inner series. type MinSeries struct { @@ -60,6 +63,14 @@ func (ms *MinSeries) ensureMinValue() { } } +// Validate validates the series. +func (ms *MinSeries) Validate() error { + if ms.InnerSeries == nil { + return fmt.Errorf("min series requires InnerSeries to be set") + } + return nil +} + // MaxSeries draws a horizontal line at the maximum value of the inner series. type MaxSeries struct { Name string @@ -117,3 +128,11 @@ func (ms *MaxSeries) ensureMaxValue() { ms.maxValue = &maxValue } } + +// Validate validates the series. +func (ms *MaxSeries) Validate() error { + if ms.InnerSeries == nil { + return fmt.Errorf("max series requires InnerSeries to be set") + } + return nil +} diff --git a/pie_chart.go b/pie_chart.go index b2be1a6..c2d485c 100644 --- a/pie_chart.go +++ b/pie_chart.go @@ -66,7 +66,7 @@ func (pc PieChart) GetHeight() int { // Render renders the chart with the given renderer to the given io.Writer. func (pc PieChart) Render(rp RendererProvider, w io.Writer) error { if len(pc.Values) == 0 { - return errors.New("Please provide at least one value.") + return errors.New("please provide at least one value") } r, err := rp(pc.GetWidth(), pc.GetHeight()) diff --git a/series.go b/series.go index 6145dcb..7986d01 100644 --- a/series.go +++ b/series.go @@ -5,5 +5,6 @@ type Series interface { GetName() string GetYAxis() YAxisType GetStyle() Style + Validate() error Render(r Renderer, canvasBox Box, xrange, yrange Range, s Style) } diff --git a/sma_series.go b/sma_series.go index a7e0034..d9f7f13 100644 --- a/sma_series.go +++ b/sma_series.go @@ -1,5 +1,7 @@ package chart +import "fmt" + const ( // DefaultSimpleMovingAveragePeriod is the default number of values to average. DefaultSimpleMovingAveragePeriod = 16 @@ -88,3 +90,11 @@ func (sma SMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, def style := sma.Style.InheritFrom(defaults) Draw.LineSeries(r, canvasBox, xrange, yrange, style, sma) } + +// Validate validates the series. +func (sma SMASeries) Validate() error { + if sma.InnerSeries == nil { + return fmt.Errorf("sma series requires InnerSeries to be set") + } + return nil +} diff --git a/stacked_bar_chart.go b/stacked_bar_chart.go index 511be46..16c4919 100644 --- a/stacked_bar_chart.go +++ b/stacked_bar_chart.go @@ -94,7 +94,7 @@ func (sbc StackedBarChart) GetBarSpacing() int { // Render renders the chart with the given renderer to the given io.Writer. func (sbc StackedBarChart) Render(rp RendererProvider, w io.Writer) error { if len(sbc.Bars) == 0 { - return errors.New("Please provide at least one bar.") + return errors.New("please provide at least one bar") } r, err := rp(sbc.GetWidth(), sbc.GetHeight()) diff --git a/time_series.go b/time_series.go index 025e86a..edade97 100644 --- a/time_series.go +++ b/time_series.go @@ -1,6 +1,9 @@ package chart -import "time" +import ( + "fmt" + "time" +) // TimeSeries is a line on a chart. type TimeSeries struct { @@ -59,3 +62,15 @@ func (ts TimeSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, def style := ts.Style.InheritFrom(defaults) Draw.LineSeries(r, canvasBox, xrange, yrange, style, ts) } + +// Validate validates the series. +func (ts TimeSeries) Validate() error { + if len(ts.XValues) == 0 { + return fmt.Errorf("time series must have xvalues set") + } + + if len(ts.YValues) == 0 { + return fmt.Errorf("time series must have yvalues set") + } + return nil +} diff --git a/time_series_test.go b/time_series_test.go index 3194997..717477b 100644 --- a/time_series_test.go +++ b/time_series_test.go @@ -28,3 +28,42 @@ func TestTimeSeriesGetValue(t *testing.T) { assert.NotZero(x0) assert.Equal(1.0, y0) } + +func TestTimeSeriesValidate(t *testing.T) { + assert := assert.New(t) + + cs := TimeSeries{ + Name: "Test Series", + XValues: []time.Time{ + time.Now().AddDate(0, 0, -5), + time.Now().AddDate(0, 0, -4), + time.Now().AddDate(0, 0, -3), + time.Now().AddDate(0, 0, -2), + time.Now().AddDate(0, 0, -1), + }, + YValues: []float64{ + 1.0, 2.0, 3.0, 4.0, 5.0, + }, + } + assert.Nil(cs.Validate()) + + cs = TimeSeries{ + Name: "Test Series", + XValues: []time.Time{ + time.Now().AddDate(0, 0, -5), + time.Now().AddDate(0, 0, -4), + time.Now().AddDate(0, 0, -3), + time.Now().AddDate(0, 0, -2), + time.Now().AddDate(0, 0, -1), + }, + } + assert.NotNil(cs.Validate()) + + cs = TimeSeries{ + Name: "Test Series", + YValues: []float64{ + 1.0, 2.0, 3.0, 4.0, 5.0, + }, + } + assert.NotNil(cs.Validate()) +}