weather.GetWeatherImage   F
last analyzed

Complexity

Conditions 14

Size

Total Lines 141
Code Lines 92

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 92
nop 1
dl 0
loc 141
rs 2.7818
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like weather.GetWeatherImage often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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)), 100, 120, 0.5, 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, 285, 0, 0.5)
166
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[2].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 385, 0, 0.5)
167
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[3].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 485, 0, 0.5)
168
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[4].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 585, 0, 0.5)
169
170
	// Humidity and cloudiness
171
	if err := gc.LoadFontFace("lato.ttf", 20); err != nil {
172
		panic(err)
173
	}
174
	gc.SetRGBA(1, 1, 1, 0.5)
175
176
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[1].Main.Humidity), 100, 315, 0, 0.5)
177
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[2].Main.Humidity), 100, 415, 0, 0.5)
178
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[3].Main.Humidity), 100, 515, 0, 0.5)
179
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[4].Main.Humidity), 100, 615, 0, 0.5)
180
181
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[1].Clouds.All)), 170, 315, 0, 0.5)
182
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[2].Clouds.All)), 170, 415, 0, 0.5)
183
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[3].Clouds.All)), 170, 515, 0, 0.5)
184
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[4].Clouds.All)), 170, 615, 0, 0.5)
185
186
	gc.SetRGBA(1, 1, 1, 1)
187
	if err := gc.LoadFontFace("lato.ttf", 50); err != nil {
188
		panic(err)
189
	}
190
191
	// Temperature
192
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[1].Main.TempMin)), 320, 300, 0.5, 0.5)
193
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[2].Main.TempMin)), 320, 400, 0.5, 0.5)
194
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[3].Main.TempMin)), 320, 500, 0.5, 0.5)
195
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[4].Main.TempMin)), 320, 600, 0.5, 0.5)
196
197
	if err := gc.LoadFontFace("owfont-regular.ttf", 60); err != nil {
198
		panic(err)
199
	}
200
201
	// Weather icon
202
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[1].WDesc[0].Id)), 20, 300, 0, 0.7)
203
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[2].WDesc[0].Id)), 20, 400, 0, 0.7)
204
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[3].WDesc[0].Id)), 20, 500, 0, 0.7)
205
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[4].WDesc[0].Id)), 20, 600, 0, 0.7)
206
207
	buf = new(bytes.Buffer)
208
	pngerr := png.Encode(buf, gc.Image())
209
	if pngerr != nil {
210
		fmt.Printf("Image: %v", pngerr)
211
	}
212
	return
213
}
214