Passed
Push — master ( 9bd27c...f2edaf )
by Viktor
01:52
created

weather.DrawOne   B

Complexity

Conditions 5

Size

Total Lines 45
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 27
nop 5
dl 0
loc 45
rs 8.7653
c 0
b 0
f 0
1
package weather
2
3
import (
4
	"bytes"
5
	"encoding/json"
6
	"fmt"
7
	"image/png"
8
	"net/http"
9
	"strings"
10
	"time"
11
12
	"github.com/FlameInTheDark/dtbot/api/location"
13
	"github.com/FlameInTheDark/dtbot/bot"
14
	"github.com/fogleman/gg"
15
)
16
17
// Forecast Weather forecast struct
18
type Forecast struct {
19
	Cod     string        `json:"cod"`
20
	Weather []WeatherData `json:"list"`
21
	City    CityData      `json:"city"`
22
}
23
24
// WeatherData Weather data struct
25
type WeatherData struct {
0 ignored issues
show
introduced by
type name will be used as weather.WeatherData by other packages, and that stutters; consider calling this Data
Loading history...
26
	Time   int64       `json:"dt"`
27
	Main   MainData    `json:"main"`
28
	Wind   WindData    `json:"wind"`
29
	Clouds CloudsData  `json:"clouds"`
30
	WDesc  []WDescData `json:"weather"`
31
}
32
33
// TZTime returns time in specified timezone
34
func (w WeatherData) TZTime(tz int) time.Time {
35
	return time.Unix(w.Time, 0).UTC().Add(time.Hour * time.Duration(tz))
36
}
37
38
// WDescData Weather description struct
39
type WDescData struct {
40
	Id   int64  `json:"id"`
0 ignored issues
show
introduced by
struct field Id should be ID
Loading history...
41
	Main string `json:"main"`
42
	Desc string `json:"description"`
43
	Icon string `json:"icon"`
44
}
45
46
// MainData Weather main data struct
47
type MainData struct {
48
	Temp     float64 `json:"temp"`
49
	Pressure float64 `json:"pressure"`
50
	TempMin  float64 `json:"temp_min"`
51
	TempMax  float64 `json:"temp_max"`
52
	Humidity int     `json:"humidity"`
53
}
54
55
// WindData Weather wind data struct
56
type WindData struct {
57
	Speed float64 `json:"speed"`
58
	Deg   float64 `json:"deg"`
59
}
60
61
// CloudsData Weather cloud data struct
62
type CloudsData struct {
63
	All int `json:"all"`
64
}
65
66
// CityData Weather city data struct
67
type CityData struct {
68
	Name string `json:"name"`
69
}
70
71
// GetWeatherImage returns buffer with weather image
72
func GetWeatherImage(ctx *bot.Context) (buf *bytes.Buffer, err error) {
73
	var (
74
		forecast Forecast
75
		city     = ctx.GetGuild().WeatherCity
76
	)
77
78
	if len(ctx.Args) > 0 {
79
		city = strings.Join(ctx.Args, "+")
80
	}
81
82
	loc, err := location.New(ctx.Conf.General.GeonamesUsername, city)
83
	if err != nil {
84
		fmt.Printf("Location API: %v", err)
85
		return
86
	}
87
88
	cityName := loc.Geonames[0].CountryName + ", " + loc.Geonames[0].Name
89
90
	// Get coordinates and get weather data
91
	newlat, newlng := loc.GetCoordinates()
92
	resp, err := http.Get(fmt.Sprintf("https://api.openweathermap.org/data/2.5/forecast?lat=%v&lon=%v&lang=%v&units=metric&appid=%v",
93
		newlat, newlng, ctx.Conf.General.Language, ctx.Conf.Weather.WeatherToken))
94
	if err != nil {
95
		fmt.Printf("Weather API: %v", err)
96
		return
97
	}
98
99
	err = json.NewDecoder(resp.Body).Decode(&forecast)
100
	if err != nil {
101
		fmt.Printf("Weather Decode: %v", err)
102
		return
103
	}
104
105
	gc := gg.NewContext(400, 650)
106
	gc.SetRGBA(0,0,0,0)
107
	gc.Clear()
108
109
	// Template
110
	gc.SetRGB255(242, 97, 73)
111
	gc.DrawRoundedRectangle(0, 0, 400, 650, 10)
112
	gc.Fill()
113
114
	// Weather lines
115
	gc.SetRGB255(234, 89, 65)
116
	gc.DrawRectangle(0, 250, 400, 100)
117
	gc.DrawRectangle(0, 450, 400, 100)
118
	gc.Fill()
119
120
	gc.SetLineWidth(2)
121
	gc.SetRGBA(0, 0, 0,0.05)
122
	gc.DrawLine(0, 250, 400, 250)
123
	gc.DrawLine(0, 349, 400, 348)
124
	gc.DrawLine(0, 450, 400, 450)
125
	gc.DrawLine(0, 549, 400, 548)
126
	gc.Stroke()
127
128
	// Text
129
	if err := gc.LoadFontFace("lato.ttf", 20); err != nil {
130
		panic(err)
131
	}
132
	// Header
133
	gc.SetRGBA(1, 1, 1, 0.7)
134
	gc.DrawStringAnchored(cityName, 10, 15, 0, 0.5)
135
	gc.SetRGBA(1, 1, 1, 0.4)
136
	gc.DrawStringAnchored(time.Now().Format("Jan 2, 2006"), 280, 15, 0, 0.5)
137
138
	// First weather data
139
	gc.SetRGBA(1, 1, 1, 0.5)
140
	if err := gc.LoadFontFace("lato.ttf", 30); err != nil {
141
		panic(err)
142
	}
143
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[0].TZTime(ctx.Conf.General.Timezone).Hour()), 50, 200, 0.5, 0.5)
144
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[0].Main.Humidity), 200, 200, 0.5, 0.5)
145
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[0].Clouds.All)), 350, 200, 0.5, 0.5)
146
147
	gc.SetRGBA(1, 1, 1, 1)
148
	if err := gc.LoadFontFace("lato.ttf", 90); err != nil {
149
		panic(err)
150
	}
151
152
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[0].Main.TempMin)), 30, 120, 0, 0.5)
153
154
	if err := gc.LoadFontFace("owfont-regular.ttf", 90); err != nil {
155
		panic(err)
156
	}
157
158
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[0].WDesc[0].Id)), 250, 120, 0, 0.7)
159
160
	if err := gc.LoadFontFace("lato.ttf", 30); err != nil {
161
		panic(err)
162
	}
163
164
	// Time
165
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[1].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 300, 0, 0.5)
166
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[2].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 400, 0, 0.5)
167
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[3].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 500, 0, 0.5)
168
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[4].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 600, 0, 0.5)
169
170
	if err := gc.LoadFontFace("lato.ttf", 50); err != nil {
171
		panic(err)
172
	}
173
174
	// Temperature
175
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[1].Main.TempMin)), 250, 300, 0, 0.5)
176
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[2].Main.TempMin)), 250, 400, 0, 0.5)
177
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[3].Main.TempMin)), 250, 500, 0, 0.5)
178
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[4].Main.TempMin)), 250, 600, 0, 0.5)
179
180
	if err := gc.LoadFontFace("owfont-regular.ttf", 60); err != nil {
181
		panic(err)
182
	}
183
184
	// Weather icon
185
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[1].WDesc[0].Id)), 20, 300, 0, 0.7)
186
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[2].WDesc[0].Id)), 20, 400, 0, 0.7)
187
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[3].WDesc[0].Id)), 20, 500, 0, 0.7)
188
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[4].WDesc[0].Id)), 20, 600, 0, 0.7)
189
190
	buf = new(bytes.Buffer)
191
	pngerr := png.Encode(buf, gc.Image())
192
	if pngerr != nil {
193
		fmt.Printf("Image: %v", pngerr)
194
	}
195
	return
196
}
197