Obsah
1. Tvorba grafů v jazyce Go (2.část)
2. Použití balíčků poskytovaných knihovnou plot
3. Složitější příklad založený na knihovně plot
4. Tvorba interaktivních grafů pro webové stránky
5. Postup vykreslení grafu na straně klienta ve webové stránce
6. Stažení všech potřebných knihoven
7. První graf na webové stránce vykreslený z hodnot, které jsou přímo součástí skriptu
8. Samostatný soubor s hodnotami, které se mají vykreslit do grafu
9. Získání hodnot, které se mají vykreslit, ze serveru
10. Generování hodnot pro vykreslení přímo serverem
11. Výpočet hodnot na straně serveru
12. Graf s průběhy několika funkcí, nastavení stylu vykreslování grafu
13. Výpočet hodnot, které se mají vykreslit, na základě zadaných parametrů
16. Úpravy vzhledu koláčového grafu
18. Jednoduchý příklad používající knihovnu Chart.js
19. Repositář s demonstračními příklady
1. Tvorba grafů v jazyce Go (2.část)
V dnešní části seriálu o programovacím jazyku Go si ukážeme některé další způsoby vykreslování grafů s naměřenými hodnotami či s průběhy funkcí. Navážeme tak na část předchozí, ve které jsme si popsali možnosti nabízené knihovnami glot a plot (z balíčku Gonum). Tyto knihovny slouží pro vytvoření grafů na straně serveru (či pracovního stroje uživatele), ovšem mnohdy je zapotřebí grafy vykreslit na klientovi, tj. v typickém případě ve webovém prohlížeči. Pro tento účel lze využít kombinaci několika technologií – HTTP serveru naprogramovaného v Go, vybrané knihovny pro vykreslování grafů na straně klienta (plotly, chart.js atd.) a většinou relativně jednoduchého skriptu, který načte hodnoty, jež se mají zobrazit a provede (resp. zahájí) jejich vykreslení.
Nejdříve se ovšem na chvíli vraťme ke knihovně plot z projektu Gonum. Tato knihovna se ve skutečnosti skládá ze čtyř balíčků, které jsou vypsány v následující tabulce:
# | Balíček | Stručný popis balíčku |
---|---|---|
1 | plot | základní balíček se základními strukturami a jejich metodami |
2 | plotter | konkrétní implementace rozhraní Plotter |
3 | plotutil | pomocné funkce pro vytvoření různých průběhů v grafu atd. |
4 | vg | rozhraní pro vykreslování 2D grafiky (vektorové) |
Většinou je nutné použít všechny tři balíčky, protože v balíčku plotter najdeme konkrétní implementace rozhraní Plotter a v plotutil pak jednotlivé typy průběhů, které se mohou v grafu objevit. Sice by bylo možné tyto části implementovat přímo v aplikaci (což je někdy nutné), ovšem pro běžné grafy je to zbytečná práce navíc.
2. Použití balíčků poskytovaných knihovnou plot
Minule jsme si taktéž ukázali čtveřici příkladů určených pro tvorbu jednoduchých grafů s využitím možností nabízených knihovnou plot. Na těchto příkladech je patrné, že je většinou nutné použít všechny čtyři balíčky zmíněné v úvodní kapitole, což je ukázáno na příkladu, s nímž jsme se již setkali:
package main import ( "gonum.org/v1/plot" "gonum.org/v1/plot/plotter" "gonum.org/v1/plot/plotutil" "gonum.org/v1/plot/vg" ) const resX = 20.0 / 3.0 * vg.Inch const resY = 5.0 * vg.Inch func main() { p, err := plot.New() if err != nil { panic(err) } input := [...]int32{1, 2, 4, 8, 9, 8, 4, 2, 1} points := make(plotter.XYs, len(input)) for i := range points { points[i].X = float64(i) points[i].Y = float64(input[i]) } p.Title.Text = "Plot #5" p.X.Label.Text = "X" p.Y.Label.Text = "Y" err = plotutil.AddLinePoints(p, "Measured data", points) if err != nil { panic(err) } err = p.Save(resX, resY, "plot05.png") if err != nil { panic(err) } }
Obrázek 1: Výsledek běhu předchozího demonstračního příkladu.
3. Složitější příklad založený na knihovně plot
Uveďme si ještě poněkud složitější příklad, který je inspirovaný kódem uvedeným přímo na stránce projektu Plot. V tomto příkladu jsou vykresleny průběhy tří funkcí, pokaždé s jiným stylem vykreslování a popisem jednotlivých os:
s, err := plotter.NewScatter(series1) l, err := plotter.NewLine(series2) lpLine, lpPoints, err := plotter.NewLinePoints(series3)
Dále je v příkladu ukázán způsob definice stylů vykreslování, například zde pro poslední průběh:
lpLine.Color = color.RGBA{G: 255, A: 255} lpPoints.Shape = draw.PyramidGlyph{}
Změna barvy jednotlivých průběhů je snadná:
s.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 255} l.LineStyle.Color = color.RGBA{B: 255, A: 255} lpPoints.Color = color.RGBA{R: 255, A: 255}
Úplný zdrojový kód tohoto příkladu vypadá následovně:
package main import ( "image/color" "math" "gonum.org/v1/plot" "gonum.org/v1/plot/plotter" "gonum.org/v1/plot/vg" "gonum.org/v1/plot/vg/draw" ) const points = 50 const resX = 20.0 / 3.0 * vg.Inch const resY = 5.0 * vg.Inch func fillInSeries(offset float64) plotter.XYs { series := make(plotter.XYs, points) function1 := func(t float64, offset float64) float64 { // limita if t == 0.0 { return 1.0 } return math.Sin(t-offset) / t } for i := range series { t := float64(i)*5.0*math.Pi/points + 0.4 series[i].X = t series[i].Y = function1(t, offset) } return series } func main() { series1 := fillInSeries(0) series2 := fillInSeries(math.Pi / 2.0) series3 := fillInSeries(-math.Pi / 2.0) p, err := plot.New() if err != nil { panic(err) } p.Title.Text = "Points Example" p.X.Label.Text = "X" p.Y.Label.Text = "Y" p.Add(plotter.NewGrid()) s, err := plotter.NewScatter(series1) if err != nil { panic(err) } s.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 255} l, err := plotter.NewLine(series2) if err != nil { panic(err) } l.LineStyle.Width = vg.Points(1) l.LineStyle.Dashes = []vg.Length{vg.Points(5), vg.Points(5)} l.LineStyle.Color = color.RGBA{B: 255, A: 255} lpLine, lpPoints, err := plotter.NewLinePoints(series3) if err != nil { panic(err) } lpLine.Color = color.RGBA{G: 255, A: 255} lpPoints.Shape = draw.PyramidGlyph{} lpPoints.Color = color.RGBA{R: 255, A: 255} p.Add(s, l, lpLine, lpPoints) p.Legend.Add("scatter", s) p.Legend.Add("line", l) p.Legend.Add("line points", lpLine, lpPoints) err = p.Save(resX, resY, "plot06.png") if err != nil { panic(err) } }
Obrázek 2: Výsledek běhu příkladu popsaného v této kapitole.
4. Tvorba interaktivních grafů pro webové stránky
Knihovny glot a Gonum plot, s nimiž jsme se seznámili v předchozím textu, jsou primárně určeny pro vykreslování grafů do rastrových obrázků, popř. do souborů, které mohou obsahovat vektorové kresby (SVG, PostScript, PDF). Jedná se tedy o knihovny, které jsou primárně určeny pro použití buď na pracovních stanicích uživatelů nebo na serverech – server v tomto případě připravuje již hotové grafy, které jsou na základě požadavku přeneseny ke klientovi a tam zobrazeny. V mnoha případech, zejména při implementaci různých „dashboardů“ (viz například známý projekt Grafana, který se často kombinuje s nástrojem Prometheus), je však výhodnější přenést i samotné vykreslení grafu na klienta. Server v tomto případě slouží pro výpočet (či jakékoli jiné získání) dat, která se mají v grafu vykreslit. A právě tímto způsobem, který do ekosystému, jenž je v současnosti okolo programovacího jazyka Go vytvořen, zapadá mnohem líp, se budeme zabývat v navazujících kapitolách.
5. Postup vykreslení grafu na straně klienta ve webové stránce
Celý postup vykreslení grafu na straně klienta do plochy webové stránky je ve skutečnosti poměrně jednoduchý a přímočarý:
- Server pošle klientovi kostru HTML stránky (což může být základ proSPA neboli pro Single Page Application).
- Klient si na základě obsahu této stránky vyžádá další potřebné informace, typicky zdrojový (minifikovaný) kód knihovny sloužící pro vykreslení grafů s využitím JavaScriptu a možností HTML 5 (canvas).
- Další kód umístěný na HTML stránce či v JavaScriptových zdrojových kódech vede k požadavku klienta na poslání dat, která se mají zobrazit v grafu.
- Server požadovaná data nějakým způsobem získá (například je vypočítá, načte metriky uložené v databázi atd.) a pošle je ve vhodném formátu zpět klientovi. Může například použít formát JSON, který sice není pro tyto účely příliš úsporný, ovšem pro data určená pro jednodušší grafy to nemusí být kritické.
Prakticky všechny dále uvedené demonstrační příklady se skládají ze tří částí:
- Implementace HTTP serveru, který je naprogramován v jazyce Go, protože právě Go se pro tyto účely velmi dobře hodí. Některé příklady jsou implementovány jako HTTP server, který klientovi poskytuje pouze statická data, další příklady mají přímo v serveru implementován i výpočet dat pro graf.
- HTML stránku s plochou, do které se bude vykreslovat graf. Na této stránce se načítá kód knihovny plotly (tu klientovi poskytuje HTTP server), v některých případech i kód knihovny jQuery a taktéž krátký kód napsaný v JavaScriptu, jenž je popsán v následujícím bodu.
- Třetí částí příkladu je již výše zmíněný kód naprogramovaný v JavaScriptu, který provede inicializaci plotly, vyžádá si data pro vykreslení a následně zažádá plotly o vykreslení grafu.
6. Stažení všech potřebných knihoven
Ještě před spuštěním dále popsaných demonstračních příkladů postavených nad knihovnou plotly je nutné získat minifikovanou verzi této knihovny. Některé příklady navíc používají i známou sadu pomocných funkcí jQuery. Následující program nazvaný setup.go, který je naprogramovaný v jazyce Go, zajistí jak stažení potřebných knihoven, tak i jejich rozkopírování na potřebná místa (ve skutečnosti se neprovádí fyzická kopie, pouze vytvoření hard linku):
package main import ( "fmt" "io" "net/http" "os" ) func downloadFile(url string, filename string) (int64, error) { resp, err := http.Get(url) if err != nil { return 0, err } defer resp.Body.Close() out, err := os.Create(filename) if err != nil { return 0, err } defer out.Close() downloaded, err := io.Copy(out, resp.Body) return downloaded, err } func performDownload(url string, filename string) { downloaded, err := downloadFile(url, filename) if err != nil { fmt.Printf("Download from URL %s failed: %v\n", url, err) return } fmt.Printf("Downloaded %d bytes from URL %s into file %s\n", downloaded, url, filename) } func createLinkInDirectory(filename string, directory string) { target := directory + filename err := os.Link(filename, target) if err != nil { fmt.Printf("Unable to create hard link from %s to %s: %v\n", filename, target, err) } } func main() { performDownload("https://code.jquery.com/jquery-3.4.1.min.js", "jquery.min.js") performDownload("https://cdn.plot.ly/plotly-latest.min.js", "plotly-latest.min.js") for example := 1; example <= 10; example++ { directory := fmt.Sprintf("plotly%02d/", example) createLinkInDirectory("jquery.min.js", directory) createLinkInDirectory("plotly-latest.min.js", directory) } }
7. První graf na webové stránce vykreslený z hodnot, které jsou přímo součástí skriptu
První aplikace, jejíž serverová část je vyvinutá v programovacím jazyku Go, je velmi jednoduchá, protože se jedná o pouhou kostru, na níž budeme později stavět komplikovanější příklady. Nejdříve si ukažme implementaci HTTP serveru. Ta je prozatím velmi stručná, protože jediným úkolem tohoto HTTP serveru je dodávat statická data uložená v podadresáři „plotly01“, v němž se nachází HTML stránka i všechny JavaScriptové soubory (knihovna plotly, popř. link na ni a námi vytvořený skript):
package main import ( "log" "net/http" ) func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly01/"))) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
V podadresáři nazvaném „plotly01“ je mj. umístěn i soubor obsahující HTML stránku, do jejíž plochy se má graf vykreslit. Povšimněte si, že do prohlížeče budeme načítat i skript s implementací knihovny plotly a taktéž náš skript pojmenovaný „plot.js“:
<!DOCTYPE html> <html> <head> <title>Plot #1</title> <meta name="Generator" content="golang"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script src="plotly-latest.min.js" language="javascript"></script> </head> <body> <h1>Plot #1</h1> <div id="graphElement" style="width:800px;height:600px;"></div> <script src="plot.js" language="javascript"></script> </body> </html>
Poslední součástí tohoto demonstračního příkladu je již výše zmíněný skript uložený do souboru „plot.js“. V tomto skriptu se přímo s využitím možností nabízených knihovnou plotly vykreslí jednoduchý graf, který vznikne vynesením a propojením pěti bodů do kartézské souřadné soustavy. Hodnoty, které mají být vykresleny, jsou v tomto případě přímo součástí skriptu – jedná se o strukturu nazvanou data:
var graphElement = document.getElementById('graphElement'); var data = { x: [1, 2, 3, 4, 5], y: [1, 2, 4, 8, 16] } var opts = { margin: { t: 0} } Plotly.plot(graphElement, [data], opts)
Za zmínku stojí fakt, že do funkce Plotly.plot se ve druhém parametru může předat více průběhů (sekvencí hodnot), které se mají zobrazit. Z tohoto důvodu je námi vytvořená struktura data (jediným) prvkem pole, které se do Plotly.plot předává. V některých dalších příkladech této vlastnosti využijeme a do jediného grafu vykreslíme několik různých průběhů, pochopitelně s odlišnými hodnotami.
Obrázek 3: Graf vykreslený knihovnou plotly přímo na plochu WWW stránky.
Obrázek 4: Jednou z vlastností knihovny plotly je i podpora pro změnu měřítka, zobrazení výřezu z grafu atd. Zde je zobrazen stejný průběh, jako na druhém obrázku, pouze s jiným měřítkem.
8. Samostatný soubor s hodnotami, které se mají vykreslit do grafu
Předchozí demonstrační příklad dokázal vykreslit graf z hodnot, které byly uloženy přímo ve zdrojovém kódu skriptu, konkrétně ve skriptu „plot.js“. To je pochopitelně příliš jednoúčelové řešení. Ve druhém demonstračním příkladu z tohoto důvodu provedeme určité zobecnění, protože hodnoty budou uloženy ve zvláštním souboru, a to konkrétně ve formátu JSON:
{ "x": [1, 2, 3, 4, 5], "y": [1, 2, 4, 8, 16] }
Samotná implementace serverové části (tj. část projektu naprogramovaná v Go) se prozatím nebude odlišovat od předchozího HTTP serveru. Jedinou změnou je, že tentokrát bude server vracet statická data (obsahy souborů) uložená v podadresáři „plotly02“ a nikoli v podadresáři „plotly01“:
package main import ( "log" "net/http" ) func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly02/"))) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
Ani HTML stránka tvořící kostru aplikace na straně klienta se příliš nezměnila. Pouze v hlavičce načítáme druhou JavaScriptovou knihovnu jQuery a ihned poté námi vytvořený skript „plot.js“ (ten se tedy nyní nenačítá až na konci stránky, protože přímo s využitím možností jQuery můžeme velmi snadno specifikovat, kdy se má část skriptu spustit):
<!DOCTYPE html> <html> <head> <title>Plot #2</title> <meta name="Generator" content="golang"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script src="plotly-latest.min.js" language="javascript"></script> <script src="jquery.min.js" language="javascript"></script> <script src="plot.js" language="javascript"></script> </head> <body> <h1>Plot #2</h1> <div id="graphElement" style="width:800px;height:600px;"></div> </body> </html>
Samotný skript „plot.js“ je však oproti prvnímu příkladu zcela odlišný. Nejdříve se pokusíme načíst obsah souboru „data.json“. Ten je uložený na serveru, který obsah dodá s využitím AJAXu (funkci XMLHttpRequest ovšem nevoláme přímo, ale využijeme zde opět možnosti jQuery). Po přijetí obsahu a jeho unmarshallingu získáme datovou strukturu, která je předána do anonymní funkce v parametru nazvaném data. Vzhledem k tomu, že obsah i formát této struktury přímo odpovídá požadavkům knihovny plotly, můžeme provést vykreslení grafu pouhými deseti řádky kódu:
$(document).ready(function() { $.getJSON("data.json", function (data) { var graphElement = $('#graphElement')[0]; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [data], opts); }); });
Obrázek 5: Předchozí příklad po vykreslení grafu do webové stránky na straně klienta.
9. Získání hodnot, které se mají vykreslit, ze serveru
Předchozí dva demonstrační příklady byly samozřejmě pro většinu použití příliš umělé, protože vyžadovaly, aby hodnoty byly dopředu připraveny ve formě statických dat, ke kterým má server přístup. Mnohdy však potřebujeme vykreslit dynamicky vytvářené a/nebo počítané hodnoty čtené ze strany serveru a posílané klientovi. Naši implementaci HTTP serveru tedy upravíme takovým způsobem, aby po přístupu na vybraný endpoint vracel generovaná data, resp. přesněji řečeno sadu x-ových a y-ových souřadnic bodů. V první variantě se bude jednat o endpoint „/data“, který bude obsluhován callback funkcí (či možná přesněji řečeno handlerem) nazvanou dataHandler. Registrace handleru vypadá následovně:
http.HandleFunc("/data", dataHandler)
Implementace handleru je prozatím opět velmi jednoduchá – nastavíme hlavičku HTTP odpovědi, kód HTTP odpovědi (zde konkrétně 200 OK) a v těle odpovědi pošleme data ve formátu JSON:
func dataHandler(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) fmt.Fprintf(writer, ` { "x": [1, 2, 3, 4, 5], "y": [1, 2, 4, 8, 16] } `) }
Úplný zdrojový kód serveru tedy může vypadat následovně:
package main import ( "fmt" "log" "net/http" ) func dataHandler(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) fmt.Fprintf(writer, ` { "x": [1, 2, 3, 4, 5], "y": [1, 2, 4, 8, 16] } `) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly03/"))) http.HandleFunc("/data", dataHandler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
Statická HTML stránka (taktéž posílaná serverem) se prakticky nezmění:
<!DOCTYPE html> <html> <head> <title>Plot #3</title> <meta name="Generator" content="golang"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script src="plotly-latest.min.js" language="javascript"></script> <script src="jquery.min.js" language="javascript"></script> <script src="plot.js" language="javascript"></script> </head> <body> <h1>Plot #3</h1> <div id="graphElement" style="width:800px;height:600px;"></div> </body> </html>
Podobný zůstane i skript, který zahájí vykreslování, nesmíme jen zapomenout uvést korektní adresu endpointu. Zde není k dalším změnám žádný důvod, protože skript neví a ani nemusí vědět, že hodnoty nejsou získány ze statického souboru, ale jsou generovány přímo serverem:
$(document).ready(function() { $.getJSON("/data", function (data) { var graphElement = $('#graphElement')[0]; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [data], opts); }); });
Obrázek 6: Výsledek spuštění třetího demonstračního příkladu používajícího knihovnu plotly.
10. Generování hodnot pro vykreslení přímo serverem
Nic nám v této chvíli nebrání upravit zdrojový kód serveru takovým způsobem, aby hodnoty, které se mají vykreslit, přímo generoval. Pro tuto funkcionalitu je nutné provést několik úprav, zejména si připravit datovou strukturu, jejíž obsah bude serializován (používá se též termín marshalling, i když má poněkud odlišný význam) do formátu JSON, který bude poslán klientovi:
type Points struct { X []int `json:"x"` Y []int `json:"y"` }
Pochopitelně se změní i obslužná rutina endpointu „/data“, a to následujícím způsobem:
func dataHandler(writer http.ResponseWriter, request *http.Request) { var points Points points.X = []int{1, 2, 3, 4, 5} points.Y = []int{1, 2, 4, 8, 16} writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(points) }
Úplný zdrojový kód takto upraveného HTTP serveru vypadá následovně:
package main import ( "encoding/json" "log" "net/http" ) type Points struct { X []int `json:"x"` Y []int `json:"y"` } func dataHandler(writer http.ResponseWriter, request *http.Request) { var points Points points.X = []int{1, 2, 3, 4, 5} points.Y = []int{1, 2, 4, 8, 16} writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(points) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly04/"))) http.HandleFunc("/data", dataHandler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
Samotný skript spouštěný na straně klienta získá přes AJAX hodnoty, které se mají vykreslit a použije tyto hodnoty při volání funkce Plotly.plot:
$(document).ready(function() { $.getJSON("/data", function (data) { var graphElement = $('#graphElement')[0]; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [data], opts); }); });
Obrázek 7: Výsledek tohoto příkladu je totožný s oběma příklady předchozími.
11. Výpočet hodnot na straně serveru
Samotný HTTP server naprogramovaný v Go může sloužit i pro výpočet hodnot, které se mají následně vykreslit v grafu. Tento přístup je ukázán v dalším příkladu, který je postaven na příkladu předchozím (například se používá totožná datová struktura Point). Rozdílná je však implementace handleru, který poskytuje hodnoty pro vykreslení:
func dataHandler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints) writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(points) }
Vidíme, že se zde již nenaplňují dva řezy s hodnotami konstantami, ale volá se jiná funkce, která tyto řezy inicializuje a naplní vhodnými daty. Konkrétně se jedná o výpočet funkce sinc:
func makePoints(npoints uint) Points { var points Points points.X = make([]float64, npoints) points.Y = make([]float64, npoints) for i := uint(0); i < npoints; i++ { t := float64(i) * periods * 2.0 * math.Pi / float64(npoints) points.X[i] = t // limita if t == 0.0 { points.Y[i] = 1.0 } else { points.Y[i] = math.Sin(t) / t } } return points }
Opět si pochopitelně ukážeme úplný zdrojový kód tohoto příkladu (resp. jeho serverovou část):
package main import ( "encoding/json" "log" "math" "net/http" ) type Points struct { X []float64 `json:"x"` Y []float64 `json:"y"` } const npoints = 100 const periods = 3 func makePoints(npoints uint) Points { var points Points points.X = make([]float64, npoints) points.Y = make([]float64, npoints) for i := uint(0); i < npoints; i++ { t := float64(i) * periods * 2.0 * math.Pi / float64(npoints) points.X[i] = t // limita if t == 0.0 { points.Y[i] = 1.0 } else { points.Y[i] = math.Sin(t) / t } } return points } func dataHandler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints) writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(points) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly05/"))) http.HandleFunc("/data", dataHandler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
Obrázek 8: Graf se zobrazením hodnot, které byly vypočteny na straně serveru.
12. Graf s průběhy několika funkcí, nastavení stylu vykreslování grafu
V dalším demonstračním příkladu si ukážeme, jakým způsobem je možné do jednoho grafu vykreslit několik funkcí. Podobně jako v předchozích příkladech si nejprve ukážeme implementaci HTTP serveru. Ten nyní obsahuje upravený výpočet funkce sinc, která je volitelně vykreslena se zvoleným offsetem (ovšem pouze u úhlu, nikoli ve jmenovateli):
func makePoints(npoints uint, offset float64) Points { var points Points points.X = make([]float64, npoints) points.Y = make([]float64, npoints) for i := uint(0); i < npoints; i++ { t := float64(i) * periods * 2.0 * math.Pi / float64(npoints) points.X[i] = t // limita if t == 0.0 { points.Y[i] = 1.0 } else { points.Y[i] = math.Sin(t+offset) / t } } return points }
Vytvořena je trojice handlerů, přičemž každý bude dodávat odlišné hodnoty. Tyto handlery jsou zaregistrovány pro tři odlišné endpointy:
func series1Handler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints, 0) writePoints(writer, points) } func series2Handler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints, npoints/2.0) writePoints(writer, points) } func series3Handler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints, -npoints/2.0) writePoints(writer, points) }
Pro jistotu bude celá implementace serveru znovu vypsána:
package main import ( "encoding/json" "log" "math" "net/http" ) type Points struct { X []float64 `json:"x"` Y []float64 `json:"y"` } const npoints = 100 const periods = 2 func makePoints(npoints uint, offset float64) Points { var points Points points.X = make([]float64, npoints) points.Y = make([]float64, npoints) for i := uint(0); i < npoints; i++ { t := float64(i) * periods * 2.0 * math.Pi / float64(npoints) points.X[i] = t // limita if t == 0.0 { points.Y[i] = 1.0 } else { points.Y[i] = math.Sin(t+offset) / t } } return points } func writePoints(writer http.ResponseWriter, points Points) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(points) } func series1Handler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints, 0) writePoints(writer, points) } func series2Handler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints, npoints/2.0) writePoints(writer, points) } func series3Handler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints, -npoints/2.0) writePoints(writer, points) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly06/"))) http.HandleFunc("/series1", series1Handler) http.HandleFunc("/series2", series2Handler) http.HandleFunc("/series3", series3Handler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
Změní se i skript spouštěný na straně klienta, protože je nutné načíst všechny tři série hodnot. Pro jednoduchost je provádí synchronní čtení dat (s čekáním na dokončení přenosu), což nemusí být ve všech aplikacích ideální řešení. Samotné vykreslení obstará jediné volání funkce Plotly.plot:
$(document).ready(function() { $.ajaxSetup({ async: false }); var series1, series2, series3; $.getJSON("/series1", function (data) { series1 = data; console.log(series1); }); $.getJSON("/series2", function (data) { series2 = data; console.log(series2); }); $.getJSON("/series3", function (data) { series3 = data; console.log(series3); }); var graphElement = $('#graphElement')[0]; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [series1, series2, series3], opts); });
Obrázek 9: Graf se třemi průběhy.
Obrázek 10: Zobrazení podrobnějších informací z vybrané oblasti grafu.
Obrázek 11: Zobrazení podrobnějších informací z vybrané oblasti grafu.
13. Výpočet hodnot, které se mají vykreslit, na základě zadaných parametrů
V dalším příkladu je již ukázáno praktičtější použití technik, s nimiž jsme se seznámili v rámci předchozích kapitol. Zde HTTP server slouží nejenom k poskytování potřebných hodnot pro graf, ale i pro jejich výpočet (jako v předchozím příkladu), přičemž parametry pro výpočet jsou získány z parametrů předaných v požadavku (request):
offsetStr := request.URL.Query().Get("offset") offset, err := strconv.ParseFloat(offsetStr, 64) if err != nil { writer.WriteHeader(http.StatusBadRequest) return } points := makePoints(npoints, offset) writePoints(writer, points)
Úplný kód serveru:
package main import ( "encoding/json" "log" "math" "net/http" "strconv" ) type Points struct { X []float64 `json:"x"` Y []float64 `json:"y"` } const npoints = 100 const periods = 2 func makePoints(npoints uint, offset float64) Points { var points Points points.X = make([]float64, npoints) points.Y = make([]float64, npoints) for i := uint(0); i < npoints; i++ { t := float64(i) * periods * 2.0 * math.Pi / float64(npoints) points.X[i] = t // limita if t == 0.0 { points.Y[i] = 1.0 } else { points.Y[i] = math.Sin(t+offset) / t } } return points } func writePoints(writer http.ResponseWriter, points Points) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(points) } func seriesHandler(writer http.ResponseWriter, request *http.Request) { offsetStr := request.URL.Query().Get("offset") offset, err := strconv.ParseFloat(offsetStr, 64) if err != nil { writer.WriteHeader(http.StatusBadRequest) return } points := makePoints(npoints, offset) writePoints(writer, points) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly07/"))) http.HandleFunc("/series", seriesHandler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
Parametry ovlivňující průběh vykreslované funkce jsou vytvořeny na straně klienta, zde (poněkud uměle) přímo ve funkci zavolané po inicializaci stránky:
$(document).ready(function() { $.ajaxSetup({ async: false }); var series1, series2, series3; $.getJSON("/series?offset=0", function (data) { series1 = data; series1.mode = 'lines' }); $.getJSON("/series?offset=-3.14", function (data) { series2 = data; series2.mode = 'markers' }); $.getJSON("/series?offset=0.25", function (data) { series3 = data; series3.mode = 'lines+markers' }); var graphElement = $('#graphElement')[0]; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [series1, series2, series3], opts); });
Obrázek 12: Průběhy tří funkcí vykreslené příkladem.
14. Sloupcový graf
Dalším často používaným typem grafu je sloupcový graf. Ten lze vykreslit jednoduše – postačuje u vybrané sekvence hodnot nastavit atribut type na hodnotu (řetězec) „bar“ tak, jak je to ukázáno v následujícím skriptu:
$(document).ready(function() { $.ajaxSetup({ async: false }); var series1, series2; $.getJSON("/series?offset=-3.14", function (data) { series1 = data; series1.type = 'scatter'; }); $.getJSON("/series?offset=0", function (data) { series2 = data; series2.type = 'bar'; }); var graphElement = $('#graphElement')[0]; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [series1, series2], opts); });
Obrázek 13: Kombinace sloupcového grafu a grafu s lomenou čarou.
15. Koláčový graf
Třetím typem grafu, který zná prakticky každý, je koláčový graf. Na straně klienta, konkrétně v skriptu plot.js, je vytvoření koláčového grafu snadné, ostatně se postačuje podívat na zdrojový kód:
$(document).ready(function() { $.getJSON("/values", function (data) { var graphElement = $('#graphElement')[0]; var pie = {}; pie.values = data; pie.labels = ['Go', 'Rust', 'C', 'Java']; pie.type = 'pie'; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [pie], opts); }); });
Výše uvedený skript očekává, že ze serveru získá čtveřici hodnot, které ovlivňují relativní velikost kruhových výseků na koláčovém grafu. Tyto hodnoty jsou poskytovány HTTP serverem, zde konkrétně v handleru pojmenovaném valuesHandler:
package main import ( "encoding/json" "log" "net/http" ) func valuesHandler(writer http.ResponseWriter, request *http.Request) { values := []float64{20, 30, 40, 50} writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(values) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("plotly09/"))) http.HandleFunc("/values", valuesHandler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
Obrázek 14: Koláčový graf.
16. Úpravy vzhledu koláčového grafu
Tato kapitola bude velmi stručná, protože si v ní pouze ukážeme, jak lze změnit vzhled koláčového grafu, konkrétně změnit kruhové výseče na části prstence. Vzhled grafu se řídí atributem hole udávajícím poměr vnitřního a vnějšího poloměru prstence:
$(document).ready(function() { $.getJSON("/values", function (data) { var graphElement = $('#graphElement')[0]; var pie = {}; pie.values = data; pie.labels = ['Go', 'Rust', 'C', 'Java']; pie.type = 'pie'; pie.hole = 0.4; var opts = { margin: { t: 0} }; Plotly.plot(graphElement, [pie], opts); }); });
Obrázek 15: Koláčový graf s modifikovaným vzhledem.
17. Knihovna Chart.js
Druhou knihovnou určenou pro vykreslení grafů na straně klienta, s níž se dnes alespoň ve stručnosti seznámíme, je knihovna nazvaná Chart.js. Kromě klasických grafů (bodový graf, graf s lomenou čarou, sloupcový graf, koláčový graf) je možné vytvořit i animované grafy, což je však téma svým rozsahem určené pro samostatný článek. Dnes si ukážeme jednoduchý příklad, který se opět bude skládat z HTTP serveru a klientské části. Nejdříve si ukažme HTTP server. V něm se (opět) počítají hodnoty, které se mají vykreslit, ve funkci makePoints:
package main import ( "encoding/json" "log" "math" "net/http" ) type Points struct { X []float64 `json:"x"` Y []float64 `json:"y"` } const npoints = 40 const periods = 3 func makePoints(npoints uint) Points { var points Points points.X = make([]float64, npoints) points.Y = make([]float64, npoints) for i := uint(0); i < npoints; i++ { t := float64(i) * periods * 2.0 * math.Pi / float64(npoints) points.X[i] = t // limita if t == 0.0 { points.Y[i] = 1.0 } else { points.Y[i] = math.Sin(t) / t } } return points } func valuesHandler(writer http.ResponseWriter, request *http.Request) { points := makePoints(npoints) writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) json.NewEncoder(writer).Encode(points) } func startHttpServer(address string) { log.Printf("Starting server on address %s", address) http.Handle("/", http.FileServer(http.Dir("chart-js/"))) http.HandleFunc("/values", valuesHandler) http.ListenAndServe(address, nil) } func main() { startHttpServer(":8080") }
type Points struct { X []float64 `json:"x"` Y []float64 `json:"y"` }
18. Jednoduchý příklad používající knihovnu Chart.js
Následuje statická HTML stránka poskytovaná HTTP serverem. V ní je vytvořen element typu canvas:
<!DOCTYPE html> <html> <head> <title>Plot #11 - chart.js</title> <meta name="Generator" content="golang"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script src="Chart.min.js" language="javascript"></script> <script src="jquery.min.js" language="javascript"></script> <script src="plot.js" language="javascript"></script> </head> <body> <h1>Plot #11 - chart.js</h1> <canvas id="graphElement" width="600px" height="600px"></canvas> </body> </html>
O samotné vykreslení se postará skript plot.js s tímto obsahem:
$(document).ready(function() { $.getJSON("/values", function (data) { var graphElement = $('#graphElement')[0]; var context = graphElement.getContext('2d'); var myChart = new Chart(context, { type: 'bar', data: { labels: data.x, datasets: [{ label: '# of Votes', data: data.y, borderWidth: 1 }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero: true } }] }, maintainAspectRatio: false, } }); }); });
Obrázek 16: Graf vykreslený knihovnou Chart.js.
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně pět až šest megabajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Go is on a Trajectory to Become the Next Enterprise Programming Language
https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 10 tools written in Go that every developer needs to know
https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 5 simple tips and tricks for writing unit tests in #golang
https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - How the Go runtime implements maps efficiently (without generics)
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/ - 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go