Hlavní navigace

Tvorba grafů v jazyce Go: kreslení ve webovém klientu

Dnes se zaměříme především na způsob vykreslování grafů na straně (webového) klienta. Go použijeme pro přípravu a přenos hodnot, které se mají vykreslit. O samotnou tvorbu grafů se stará příslušná JS knihovna, například plotly.
Pavel Tišnovský
Doba čtení: 48 minut

Sdílet

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ů

14. Sloupcový graf

15. Koláčový graf

16. Úpravy vzhledu koláčového grafu

17. Knihovna Chart.js

18. Jednoduchý příklad používající knihovnu Chart.js

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

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.

Poznámka: v případě, že se chystáte zobrazit pouze jediný graf (například nějaký sloupcový graf s výsledky benchmarků, koláčový graf atd.), nemusí být využití JavaScriptové knihovny z pohledu klienta vždy rychlejší, protože samotný kód knihovny může být velmi rozsáhlý (dále použitá knihovna plotly má v minifikované verzi cca 3MB), takže například graf přenesený v SVG (tedy ve vektorové podobě) může být na klientovi vykreslen rychleji. Ovšem ztrácíme tím možnou interaktivitu – u dalších příkladů si totiž po jejich spuštění a zobrazení ve webovém prohlížeči můžete graf různě zvětšovat, po najetí myší na průběh se zobrazí hodnota v daném místě grafu (popř. hodnoty všech průběhů pro danou hodnotu nezávislé proměnné), graf je možné uložit přímo na straně klienta v různých formátech atd.

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ý:

  1. Server pošle klientovi kostru HTML stránky (což může být základ proSPA neboli pro Single Page Application).
  2. 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).
  3. 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.
  4. 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é.
Poznámka: v současnosti existuje takřka nepřeberné množství JavaScriptových knihoven, které tvorbu grafů na straně klienta (tedy webového prohlížeče) umožňují. Některé z těchto knihoven nabízí pouze minimální možnosti úpravy grafů – takové knihovny mají tu výhodu, že jsou relativně malé. Příkladem může být původní verze knihovny Flot, popř. knihovna graph.js, kterou si taktéž ukážeme. Jednou z nejznámějších a možná i nejuniverzálnějších knihoven tohoto typu je knihovna nazvaná plotly, jíž lze získat z adresy https://cdn.plot.ly/plotly-latest.min.js a jejíž vybrané možnosti si ukážeme v navazujících kapitolách.

Prakticky všechny dále uvedené demonstrační příklady se skládají ze tří částí:

  1. 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.
  2. 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.
  3. 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)
        }

}
Poznámka: pochopitelně je možné tento program nahradit nějakým skriptem naprogramovaným v shellu, ovšem výše uvedený program je kompatibilní i s těmi neunixovými operačními systémy, jejichž souborové systémy podporují tvoru linků.

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")
}
Poznámka: HTTP server implementovaný v Go vrací pro statické soubory korektní hlavičky.

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);
        });
});
Poznámka: jedním z důvodů, proč je v současnosti nutné použít HTTP server i pro takto jednoduché demonstrační příklady, je fakt, že moderní webové prohlížeče (pokud je nenastavíte do jiného režimu) odmítnou načítat JSON z lokálního souboru. Viz též problematika okolo CORS (obecně je však toto chování prohlížečů bezpečnější, než tomu bylo v minulosti).

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"`
}
Poznámka: povšimněte si, že musíme uvést i příslušné jméno atributu v JSONu, protože prvky struktury musí být v jazyce Go psány velkými písmeny (jinak budou při serializaci ignorovány).

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")
}
Poznámka: část, která je spuštěna na klientovi, je prakticky totožná s předchozími příklady a proto ji zde již nebudeme uvádět.

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);
});
Poznámka: samozřejmě je možné v případě potřeby tyto parametry nastavovat na webové stránce přes formulářové prvky atd.

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);
});
Poznámka: ostatní součásti příkladu (HTTP server atd.) jsou totožné s příkladem předchozím.

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")
}
Poznámka: hodnoty jsou opět, jako v předchozích příkladech, přenášeny ve struktuře Points s dvojicí atributů X a Y:
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:

tip_Ansible

$(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:

# Příklad Stručný popis Cesta
1 plot01.go prázdný graf obsahující pouze legendu a osy https://github.com/tisnik/go-root/blob/master/article50/plot01.go
2 plot02.go graf s naměřenými hodnotami vykreslený knihovnou plot https://github.com/tisnik/go-root/blob/master/article50/plot02.go
3 plot03.go změna popisků na osách https://github.com/tisnik/go-root/blob/master/article50/plot03.go
4 plot04.go zobrazení průběhů dvou funkcí v jediném grafu https://github.com/tisnik/go-root/blob/master/article50/plot04.go
5 plot05.go další ukázka možností knihovny plot z projektu Gonum https://github.com/tisnik/go-root/blob/master/article51/plot05.go
       
6 setup.go https://github.com/tisnik/go-root/blob/master/article51/setup.go
       
7 plotly01.go server prvního příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly01.go
8 plotly02.go server druhého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly02.go
9 plotly03.go server třetího příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly03.go
10 plotly04.go server čtvrtého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly04.go
11 plotly05.go server pátého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly05.go
12 plotly06.go server šestého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly06.go
13 plotly07.go server sedmého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly07.go
14 plotly08.go server osmého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly08.go
15 plotly09.go server devátého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly09.go
16 plotly10.go server desátého příkladu používajícího knihovnu Plotly https://github.com/tisnik/go-root/blob/master/article51/plo­tly10.go
       
17 chart_js.go server příkladu používajícího knihovnu Chart.js https://github.com/tisnik/go-root/blob/master/article51/char­t_js.go
Poznámka: první čtyři příklady jsou uvedeny jen pro úplnost, protože jsme se jejich podrobnějším popisem zabývali v předchozí části seriálu o programovacím jazyce Go.

20. Odkazy na Internetu

  1. Stránka projektu plotly
    https://plot.ly/
  2. Plotly JavaScript Open Source Graphing Library
    https://plot.ly/javascript/
  3. Domain coloring
    https://en.wikipedia.org/wi­ki/Domain_coloring
  4. Michael Fogleman's projects
    https://www.michaelfogleman­.com/projects/tagged/grap­hics/
  5. Color Graphs of Complex Functions
    https://web.archive.org/web/20120511021419/htt­p://w.american.edu/cas/mat­hstat/lcrone/ComplexPlot.html
  6. A Gallery of Complex Functions
    http://wismuth.com/complex/ga­llery.html
  7. package glot
    https://godoc.org/github.com/A­rafatk/glot
  8. Gnuplotting: Output terminals
    http://www.gnuplotting.org/output-terminals/
  9. Introducing Glot the plotting library for Golang
    https://medium.com/@Arafat­./introducing-glot-the-plotting-library-for-golang-3133399948a1
  10. Introducing Glot the plotting library for Golang
    https://blog.gopheracademy.com/advent-2018/introducing-glot/
  11. Glot is a plotting library for Golang built on top of gnuplot
    https://github.com/Arafatk/glot
  12. Example plots (gonum/plot)
    https://github.com/gonum/plot/wi­ki/Example-plots
  13. A repository for plotting and visualizing data (gonum/plot)
    https://github.com/gonum/plot
  14. golang library to make https://chartjs.org/ plots
    https://github.com/brentp/go-chartjs
  15. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  16. The Gonum Numerical Computing Package
    https://www.gonum.org/pos­t/introtogonum/
  17. Gomacro na GitHubu
    https://github.com/cosmos72/gomacro
  18. gophernotes – Use Go in Jupyter notebooks and nteract
    https://github.com/gopher­data/gophernotes
  19. gonum
    https://github.com/gonum
  20. go-gota/gota – DataFrames and data wrangling in Go (Golang)
    https://porter.io/github.com/go-gota/gota
  21. A repository for plotting and visualizing data
    https://github.com/gonum/plot
  22. Gonum Numerical Packages
    https://www.gonum.org/
  23. Stránky projektu MinIO
    https://min.io/
  24. MinIO Quickstart Guide
    https://docs.min.io/docs/minio-quickstart-guide.html
  25. MinIO Go Client API Reference
    https://docs.min.io/docs/golang-client-api-reference
  26. MinIO Python Client API Reference
    https://docs.min.io/docs/python-client-api-reference.html
  27. 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/
  28. Benchmarking MinIO vs. AWS S3 for Apache Spark
    https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/
  29. MinIO Client Quickstart Guide
    https://docs.min.io/docs/minio-client-quickstart-guide.html
  30. Analýza kvality zdrojových kódů Minia
    https://goreportcard.com/re­port/github.com/minio/minio
  31. This is MinIO
    https://www.youtube.com/wat­ch?v=vF0lQh0XOCs
  32. Running MinIO Standalone
    https://www.youtube.com/wat­ch?v=dIQsPCHvHoM
  33. „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
    https://www.youtube.com/wat­ch?v=wlpn8K0jJ4U
  34. Ginkgo
    http://onsi.github.io/ginkgo/
  35. Gomega
    https://onsi.github.io/gomega/
  36. Ginkgo's Preferred Matcher Library na GitHubu
    https://github.com/onsi/gomega/
  37. Provided Matchers
    http://onsi.github.io/gomega/#provided-matchers
  38. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  39. Balíček goexpect
    https://github.com/google/goexpect
  40. Balíček go-expect
    https://github.com/Netflix/go-expect
  41. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  42. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  43. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  44. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  45. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  46. goblin na GitHubu
    https://github.com/franela/goblin
  47. Mocha framework
    https://mochajs.org/
  48. frisby na GitHubu
    https://github.com/verdverm/frisby
  49. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  50. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  51. Cucumber for golang
    https://github.com/DATA-DOG/godog
  52. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  53. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  54. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  55. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  56. xml.org
    http://www.xml.org/
  57. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  58. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  59. JSON to YAML
    https://www.json2yaml.com/
  60. Data Format Converter
    https://toolkit.site/format.html
  61. Viper na GitHubu
    https://github.com/spf13/viper
  62. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  63. The fantastic ORM library for Golang
    http://gorm.io/
  64. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  65. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  66. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  67. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  68. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  69. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  70. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  71. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  72. Sémantické verzování
    https://semver.org/
  73. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  74. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  75. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  76. Modules
    https://github.com/golang/go/wi­ki/Modules
  77. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  78. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  79. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  80. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  81. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  82. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  83. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  84. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  85. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  86. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  87. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  88. gopls
    https://github.com/golang/go/wi­ki/gopls
  89. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  90. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  91. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  92. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  93. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  94. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  95. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  96. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  97. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  98. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  99. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  100. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  101. Package trace
    https://golang.org/pkg/runtime/trace/
  102. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  103. Command trace
    https://golang.org/cmd/trace/
  104. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  105. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  106. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  107. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  108. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  109. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  110. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  111. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  112. 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/
  113. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  114. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  115. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  116. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  117. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  118. The LLDB Debugger
    http://lldb.llvm.org/
  119. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  120. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  121. 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
  122. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  123. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  124. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  125. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  126. 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/
  127. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  128. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  129. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  130. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  131. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  132. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  133. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  134. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  135. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  136. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  137. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  138. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  139. go-cron
    https://github.com/rk/go-cron
  140. gocron
    https://github.com/jasonlvhit/gocron
  141. clockwork
    https://github.com/whiteShtef/cloc­kwork
  142. clockwerk
    https://github.com/onatm/clockwerk
  143. JobRunner
    https://github.com/bamzi/jobrunner
  144. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  145. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  146. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  147. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  148. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  149. go-prompt
    https://github.com/c-bata/go-prompt
  150. readline
    https://github.com/chzyer/readline
  151. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  152. go-readline
    https://github.com/fiorix/go-readline
  153. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  154. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  155. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  156. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  157. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  158. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  159. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  160. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  161. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  162. Editline Library (libedit)
    http://thrysoee.dk/editline/
  163. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  164. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  165. WinEditLine
    http://mingweditline.sourceforge.net/
  166. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  167. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  168. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  169. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  170. history(3) – Linux man page
    https://linux.die.net/man/3/history
  171. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  172. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  173. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  174. Balíček ogletest
    https://github.com/jacobsa/ogletest
  175. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  176. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  177. package testing
    https://golang.org/pkg/testing/
  178. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  179. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  180. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  181. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  182. GoConvey
    http://goconvey.co/
  183. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  184. 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
  185. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  186. package gg
    https://godoc.org/github.com/fo­gleman/gg
  187. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  188. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  189. The Go image package
    https://blog.golang.org/go-image-package
  190. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  191. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  192. YAML
    https://yaml.org/
  193. edn
    https://github.com/edn-format/edn
  194. Smile
    https://github.com/FasterXML/smile-format-specification
  195. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  196. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  197. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  198. Introducing JSON
    http://json.org/
  199. Package json
    https://golang.org/pkg/encoding/json/
  200. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  201. Go by Example: JSON
    https://gobyexample.com/json
  202. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  203. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  204. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  205. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  206. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  207. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  208. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  209. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  210. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  211. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  212. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  213. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  214. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  215. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  216. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  217. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  218. Algorithms to Go
    https://yourbasic.org/
  219. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  220. 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/
  221. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  222. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  223. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  224. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  225. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  226. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  227. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  228. The Go Programming Language (home page)
    https://golang.org/
  229. GoDoc
    https://godoc.org/
  230. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  231. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  232. The Go Programming Language Specification
    https://golang.org/ref/spec
  233. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  234. Package builtin
    https://golang.org/pkg/builtin/
  235. Package fmt
    https://golang.org/pkg/fmt/
  236. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  237. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  238. Learning Go
    https://www.miek.nl/go/
  239. Go Bootcamp
    http://www.golangbootcamp.com/
  240. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  241. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  242. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  243. The Go Blog
    https://blog.golang.org/
  244. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  245. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  246. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  247. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  248. 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
  249. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  250. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  251. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  252. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  253. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  254. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  255. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  256. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  257. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  258. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  259. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  260. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  261. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  262. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  263. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  264. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  265. 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/
  266. 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
  267. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  268. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  269. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  270. Go vs. Python
    https://www.peterbe.com/plog/govspy
  271. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  272. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  273. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  274. Go by Example: Slices
    https://gobyexample.com/slices
  275. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  276. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  277. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  278. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  279. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  280. nils In Go
    https://go101.org/article/nil.html
  281. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  282. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  283. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  284. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  285. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  286. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  287. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  288. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  289. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  290. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  291. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  292. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  293. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  294. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  295. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  296. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  297. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  298. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  299. 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
  300. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  301. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  302. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  303. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  304. Selectors
    https://golang.org/ref/spec#Selectors
  305. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  306. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  307. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  308. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  309. Part 21: Goroutines
    https://golangbot.com/goroutines/
  310. Part 22: Channels
    https://golangbot.com/channels/
  311. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  312. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  313. 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/
  314. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  315. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  316. Control Structures
    https://www.golang-book.com/books/intro/5
  317. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  318. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  319. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  320. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  321. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  322. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  323. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  324. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  325. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  326. Effective Go
    https://golang.org/doc/ef­fective_go.html
  327. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  328. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  329. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  330. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  331. Nils in Go
    https://www.doxsey.net/blog/nils-in-go