Matematika v příkazové řádce XV

10. 5. 2006
Doba čtení: 12 minut

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
V patnácté - předposlední - části seriálu, který je věnován matematicky zaměřeným aplikacím ovládaným zejména z příkazového řádku a/nebo z celoobrazovkového textového terminálu, si povíme, jakým způsobem jsou v aplikaci gnuplot zpracována a následně vykreslena numerická data uložená v externích souborech. Vykreslení těchto dat představuje jednu z nejdůležitějších a nejpotřebnějších činností, které je možné s gnuplotem provádět.

Obsah

1. Jednoduché grafy vytvořené z externích dat
2. Jednorozměrné grafy
3. Specifikace x-ové i y-ové souřadnice bodů grafu
4. Styly vykreslovaného grafu
   4.1 Propojení bodů pomocí lomené čáry (polyčáry)
   4.2 Zobrazení jednotlivých bodů
   4.3 Zobrazení digitalizovaných signálů pomocí „schodovité“ funkce
   4.4 Zvýraznění bodů, které jsou propojeny lomenou čarou (polyčarou)
   4.5 Impulsy začínající na x-ové ose
   4.6 Sloupcový graf
5. Jednorozměrné grafy a chybové hodnoty
6. Volba výstupního grafického zařízení
7. Obsah dalšího pokračování tohoto seriálu

1. Grafy vytvořené z externích dat

V předchozí části tohoto seriálu jsme si kromě uvedení základních informací o aplikaci gnuplot ukázali i tvorbu jednorozměrných grafů funkcí jedné nezávislé a jedné závislé proměnné, přičemž vykreslované funkce byly zadány přímo svým vzorcem, například zápisem plot sin(x)/x či plot sin(x)/(x**2+1) (onou nezávislou proměnnou je v těchto případech proměnná x). Schopnosti gnuplotu jsou v této oblasti opravdu velké a nepřímo tak ukazují sílu vestavěného skriptovacího jazyka, který se snaží být konzistentní jak s programovacím jazykem C, tak i s dalšími programovacími jazyky, například Fortranem či Basicem. Avšak gnuplot disponuje i další velmi užitečnou funkcionalitou – schopností zobrazit jednorozměrné i dvojrozměrné grafy sestrojené na základě dodaných numerických hodnot uložených v externích (většinou textových – ASCII) souborech a/nebo hodnot načtených ze standardního vstupu.

V tomto případě gnuplot vlastně funguje jako souborový či datový filtr, který ze vstupu načítá numerická data a na výstup posílá vektorový nebo rastrový obrázek představující vytvořený graf, popř. graf zobrazuje na zvoleném grafickém zařízení, například v okně systému X Window. Gnuplot může tvořit doplněk k dříve popisovanému tabulkovému procesoru sc, který sám o sobě grafický výstup neimplementuje – ostatně největší síla Unixu spočívá právě v cílené kombinaci více jednoúčelových nástrojů. S pomocí externích skriptů volajících gnuplot či příkazů zapsaných v interním skriptovacím jazyce je dokonce možné provést jednoduchou animaci průběhu funkce, například v závislosti na nějaké průběžně se měnící vstupní podmínce ovlivňující hodnotu jediné nezávislé proměnné. Sérii vytvořených snímků sice gnuplot do animace sám spojit neumí, ale je možné použít další utility a vyrobit například animovaný GIF či video ve formátu MPEG.

V dalších kapitolách si ukážeme, jakým způsobem je možné v gnuplotu načítat data uložená v externích souborech a tvořit z těchto dat přehledné grafy. Základem je vytvoření jednorozměrného grafu, což je vysvětleno ve druhé kapitole. Ve třetí kapitole si ukážeme použití externích souborů obsahujících x-ové i y-ové souřadnice bodů. V mnoha případech je nutné ovlivnit způsob vykreslení grafů a popř. do grafu přidat i odchylky či chybové hodnoty – to je vysvětleno ve čtvrté a páté kapitole. Nakonec si ještě v šesté kapitole ukážeme, jakým způsobem je možné zvolit výstupní grafické zařízení nebo výstupní soubor, do kterého se bude provádět vykreslení grafu.

2. Jednorozměrné grafy

Jak jsme se již stručně zmínili v předchozí kapitole, představuje tvorba jednorozměrného grafu sestrojeného z hodnot uložených v externích souborech, základ pro naučení a používání dalších složitějších operací, například tvorby dvojrozměrných grafů nebo grafů vykreslujících vektorová pole. Externí soubory, ve kterých jsou numerická data uložena, nemusí mít nějakou speciální předem předepsanou strukturu. Právě naopak, pomocí poměrně jednoduché syntaxe (příkazem using) se gnuplot nakonfiguruje přesně podle struktury konkrétního souboru, takže je možné zpracovat data vytvořená například v dříve popsaném tabulkovém procesoru sc. V podstatě je „pouze“ nutné specifikovat, které numerické hodnoty je potřeba ze souboru načíst a v jakém formátu se mají tyto údaje načítat. Gnuplot poté automaticky provede všechny potřebné konverze dat, data načte, zpracuje a následně vytvoří (tj. vykreslí či uloží do rastrového či vektorového souboru) požadovaný graf. Ukažme si tedy jednoduchý příklad, pomocí kterého je vykreslen graf sestrojený z několika hodnot uložených v textovém souboru; tento příklad budeme postupně rozšiřovat o další možnosti a funkcionalitu.

Úplně nejjednodušší příklad načte numerické hodnoty z textového souboru, kde tyto hodnoty mohou být uloženy například systémem co hodnota, to jeden textový řádek. V případě jednorozměrných grafů může být na každém textovém řádku uložena buď jedna hodnota nebo hodnoty dvě. Pokud je na řádku uložena pouze jedna hodnota, je chápána jako y-ová souřadnice bodu, x-ovou souřadnici doplní gnuplot automaticky (předpokládá se, že za sebou jdoucí hodnoty jsou tímto způsobem uloženy i v externím souboru). V případě, že jsou na řádku uloženy hodnoty dvě, chápe gnuplot první hodnotu jako x-ovou souřadnici bodu a hodnotu druhou jako y-ovou souřadnici. Existují i další možnosti uložení dat, které se využijí například tehdy, když je zapotřebí vykreslit hodnoty a k nim příslušející vypočtené či naměřené chyby (ty je zapotřebí uložit vedle samotných hodnot – viz pátou kapitolu). Všechny zadané body se při tvorbě grafu v tom nejjednodušším případě pouze vykreslí pomocí malých kroužků a tímto způsobem jsou následně zobrazeny. Příkaz provádějící načtení hodnot z textového souboru „1.txt“ a následné vytvoření grafu bude mít tento tvar:

plot "1.txt" 

Soubor „1.txt“ byl získán následujícím programem napsaným s využitím programovacího jazyka C:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{
    float x, y;
    for (x=0.1; x<25.0; x+=0.1) {
         y=sin(x)/x;
         printf("%5.3f\n", y);
    }
    return 0;
} 

Vytvořený soubor, který byl získán přesměrováním standardního výstupního proudu pomocí prostředků shellu, má tento obsah:

0.998
0.993
0.985
0.974

...
... dalších cca dvě stě řádků s čísly
...

-0.033
-0.030
-0.027
-0.024
-0.021
-0.017
-0.013
-0.009 

Po spuštění gnuplotu a provedení příkazu plot „1.txt“ se na implicitně nastavené grafické zařízení vykreslí graf, který může vypadat například podobně jako graf zobrazený na prvním obrázku. Přesný tvar grafu je ovšem závislý na rozlišení výsledného zařízení a na jeho grafických schopnostech, například na možnostech práce s barvami či podpoře ditheringu a antialiasingu. Všechny následující obrázky byly získány ze screenshotu grafického okna X Window systému. Nastavení grafického zařízení bylo provedeno příkazem:

set term x11 

Bližší informace o nastavení výstupního zařízení budou uvedeny v šesté kapitole.

math15_1

Obrázek 1: Graf získaný z bodů uložených v externím souboru „1.txt“, ve kterém jsou specifikovány pouze jejich x-ové souřadnice

3. Specifikace x-ové i y-ové souřadnice bodů grafu

Všimněte si, že při kresbě prvního grafu nemohl gnuplot žádným způsobem zjistit x-ové souřadnice vykreslovaných bodů, protože v externím souboru byly uloženy pouze y-ové hodnoty (řídicí proměnná smyčky nebyla tištěna a tím pádem ani nemohla být přesměrována do externího souboru). Z tohoto důvodu se x-ové souřadnice před vykreslením grafu automaticky dopočítaly, a to tak, že každý následující bod měl x-ovou souřadnici o jednotku větší než bod předchozí. Pokud bychom požadovali, aby se korektně zobrazily i x-ové souřadnice, musel by se program pro tvorbu dat upravit následujícím způsobem:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{
    float x, y;
    for (x=0.1; x<25.0; x+=0.1) {
         y=sin(x)/x;
         printf("%5.3f %5.3f\n", x, y);
    }
    return 0;
} 

Jak je z výše uvedeného výpisu kódu zřejmé, úprava spočívá ve vytištění jak řídicí proměnné smyčky x, tak i hodnoty proměnné y. Graf získaný z takto vytvořených dat je zobrazený na druhém obrázku. Všimněte si, že nyní již je popis x-ové osy korektní, protože se na ní vyskytují hodnoty z intervalu (0..25) a nikoli hodnoty z automaticky vytvořeného intervalu (0..250), jak tomu bylo u předchozího gra­fu.

math15_2

Obrázek 2: Graf získaný z bodů uložených v externím souboru „1.txt“, ve kterém jsou uloženy jak x-ové, tak i y-ové souřadnice

4. Styly vykreslovaného grafu

Pokud by nám nevyhovoval způsob kreslení jednotlivých bodů, ze kterých se graf skládá, pomocí malých křížků, je možné při kresbě grafu zadat specifikátor stylu kreslení – ten rozšiřuje možnosti příkazu plot. Existuje více stylů, všechny si ukážeme na obrázcích, přičemž u každého popisu stylu bude uveden i příslušný příkaz, který vedl k tvorbě grafu. Na následujících screenshotech si všimněte, že v pravém horním rohu každého grafu je pomocí značky ukázáno, jaký styl (a mimo jiné také barva) je pro daný průběh funkce použit. To je výhodné zejména při slučování více průběhů funkcí do jednoho grafu.

4.1 Propojení bodů pomocí lomené čáry (polyčáry)

Patrně nejpoužívanějším stylem používaným pro vykreslení jednorozměrného grafu je propojení jednotlivých bodů pomocí lomené čáry (polyčáry). Při dostatečně velké hustotě bodů, resp. při jejich malé vzájemné vzdálenosti, se lomená čára opticky vyhladí a vznikne dojem hladkého průběhu funkce – samozřejmě za předpokladu, že je i průběh funkce hladký a nejsou v něm například skoky. Graf se vykreslí pomocí lomené čáry po zadání příkazu:

plot "1.txt" with lines 

Výsledný graf vzniklý po provedení tohoto příkazu je zobrazen na třetím obrázku.

math15_3

Obrázek 3: Graf funkce vykreslený pomocí lomené čáry (polyčáry)

4.2 Zobrazení jednotlivých bodů

V případě, že je hustota bodů ve vykreslovaném grafu příliš velká (v některých případech může jít i o desetitisíce bodů umístěných v jednom grafu), může být výhodné každý bod vykreslit pomocí velmi malé značky. Na obrazovce se typicky jedná o jeden jediný pixel a na tiskárně o nejmenší viditelnou stopu, která je však většinou větší než nejmenší tiskový bod. I takového efektu, který je mimo jiné ukázán na čtvrtém obrázku, je v gnuplotu samozřejmě možné dosáhnout, stačí použít příkaz:

plot "1.txt" with dots 
math15_4

Obrázek 4: Graf funkce vykreslený pomocí jednotlivých bodů (pixelů)

4.3 Zobrazení digitalizovaných signálů pomocí „schodovité“ funkce

Dále je možné zvýraznit jednotlivé hodnoty v grafu tak, že se neprovede propojení zadaných bodů pomocí lomené čáry, ale vytvoří se „schody“ mezi jednotlivými body. V některých případech, zejména při zobrazování digitalizovaných resp. diskreti­zovaných signálů je tento způsob nejbližší realitě. Příkaz pro tvorbu tohoto stylu grafu je velmi jednoduchý:

plot "1.txt" with steps 
math15_5

Obrázek 5: Graf „schodovité“ funkce

4.4 Zvýraznění bodů, které jsou propojeny lomenou čarou (polyčarou)

Pokud si přejeme zvýraznit jednotlivé zadané body, ale přitom tyto body současně navzájem propojit lomenou čarou (polyčarou), je možné použít příkaz:

plot "1.txt" with linespoints 

Výsledkem je graf, který vlastně spojuje vlastnosti grafu získaného příkazem plot „1.txt“ a plot „1.txt“ with lines.

math15_6

Obrázek 6: Graf funkce se zobrazenými body, které jsou propojeny lomenou čarou

4.5 Impulsy začínající na x-ové ose

Velmi ilustrativní může být v některých případech graf, ve kterém jednotlivé hodnoty představují impulsy začínající na x-ové ose. Příklad takového grafu je zobrazen na sedmém obrázku a příkaz, kterým byl graf vytvořen, má tvar:

plot "1.txt" with impulses 
math15_7

Obrázek 7: Graf funkce zobrazený pomocí impulsů

4.6 Sloupcový graf

Posledním typem grafu jedné nezávislé proměnné je graf sloupcový. Tento typ grafu je typicky používán v těch případech, kdy je zapotřebí zobrazit pouze malé množství hodnot, například výdaje firmy v jednotlivých měsících (celkem dvanáct hodnot) apod. Příkaz pro tvorbu sloupcového grafu má tvar:

plot "1.txt" with boxes 
math15_8

Obrázek 8: Sloupcový graf

5. Jednorozměrné grafy a chybové hodnoty

Při výpočtu či měření některých veličin se můžeme dopustit předem známé chyby nebo připouštíme určitý rozkmit hodnot. Pro účely zobrazení chybových hodnot (resp. rozptylu hodnot od hodnoty vykreslené) používá gnutplot rozšířený soubor s uloženými numerickými hodnotami. Na každém řádku vstupního textového souboru se nachází tři, čtyři nebo dokonce šest hodnot s následujícím významem:

  1. x, y, ydelta – x-ová hodnota, y-ová hodnota a symetrický rozptyl y-ové hodnoty
  2. x, y, ylow, yhigh – x-ová hodnota, y-ová hodnota, nejnižší možná a nejvyšší možná y-ová hodnota
  3. x, y, xdelta – x-ová hodnota, y-ová hodnota a symetrický rozptyl x-ové hodnoty
  4. x, y, xlow, xhigh – x-ová hodnota, y-ová hodnota, nejnižší možná a nejvyšší možná x-ová hodnota
  5. x, y, xdelta, ydelta – x-ová hodnota, y-ová hodnota, symetrický rozptyl x-ové hodnoty a symetrický rozptyl y-ové hodnoty
  6. x, y, xlow, xhigh, ylow, yhigh – x-ová hodnota, y-ová hodnota, nejnižší možná a nejvyšší možná x-ová hodnota, nejnižší možná a nejvyšší možná y-ová hodnota

Všimněte si, že ve všech případech musí být zadána i x-ová hodnota bodu, není tedy možné použít zjednodušený formát bez této hodnoty. Je to logické, protože jinak by gnuplot nedokázal rozpoznat, kde začínají a kde končí souřadnice bodů a kde naopak začínají údaje o chybových hodnotách. Následující céčkový program slouží k vytvoření souboru obsahující na každém řádku tři hodnoty, tj. bude možné použít formáty x, y, ydelta a x, y, xdelta:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{
    float x, y;
    float delta;
    for (x=0.1; x<25.0; x+=0.2) {
         y=sin(x)/x;
         delta=cos(x)/(x-10)/10.0f;
         // orezat maxima a minima
         if (delta>1.0f) delta=1.0f;
         if (delta<-1.0f) delta=-1.0f;
         printf("%5.3f %5.3f %6.4f\n", x, y, delta);
    }
    return 0;
} 

Po přesměrování výstupu do souboru vznikne soubor obsahující následující řádky:

0.100 0.998 -0.0101
0.300 0.985 -0.0098
0.500 0.959 -0.0092
0.700 0.920 -0.0082
0.900 0.870 -0.0068
1.100 0.810 -0.0051

...
... dalších sto řádků s číselnými hodnotami
...

23.500 -0.042 -0.0005
23.700 -0.042 0.0010
23.900 -0.039 0.0024
24.100 -0.036 0.0036
24.300 -0.030 0.0047
24.500 -0.024 0.0056
24.700 -0.017 0.0062
24.900 -0.009 0.0065 

S takto vytvořeným souborem je možné nakládat více způsoby. Po zadání příkazu:

plot "1.txt" with yerrorbard 

Se zobrazí graf, na kterém jsou kromě vlastního průběhu funkce zvýrazněny i odchylky od y-ové hodnoty. Tyto odchylky jsou přečteny z třetího sloupce souboru.

math15_9

Obrázek 9: Graf funkce zobrazený pomocí spolu s odchylkami y-ových hodnot

Třetí hodnotu na řádku v souboru je možné také chápat jako x-ovou odchylku, která se zobrazí pomocí příkazu:

plot "1.txt" with xerrorbard 

Výsledek je zobrazený na desátém obrázku, všimněte si také použití jiného měřítka odchylek, než tomu bylo v případě grafu zobrazeného na devátém obrázku.

math15_10

Obrázek 10: Graf funkce zobrazený pomocí spolu s odchylkami x-ových hodnot

6. Volba výstupního grafického zařízení

Při práci s gnuplotem, zejména při dávkovém zpracování mnoha numerických dat, je velmi důležité nastavení výstupního grafického zařízení nebo výstupního souboru, který bude obsahovat rastrová nebo vektorová data reprezentující vykreslený graf. Pro nastavení výstupu slouží příkaz set terminal, také je možné použít zkrácený příkaz set term. V následující tabulce jsem se pokusil vyjmenovat nejpoužívanější a nejužitečnější výstupní grafická zařízení resp. formáty výstupních souborů, které je možné použít pro uložení grafu. Je samozřejmé, že některé typy výstupů pracují pouze při splnění určitých hardwarových a softwarových podmínek, například při spuštěném systému X Window.

Výstupní grafická zařízení
Grafický výstup Příkaz pro nastavení výstupu Typ grafiky
Formát Adobe Illustratoru 3.0 set terminal aifm vektory
Výstup do okna na MS Windows set terminal windows rastr
Výstup do okna na X Window set terminal x11 rastr
Výstup na obrazovku v MS-DOSu set terminal svga rastr
Výstup do PostScriptu set terminal post vektory
Formát CorelDraw set terminal corel vektory
Formát AutoCAD DXF set terminal dxf vektory
Formát PBM (Portable Bitmap) set terminal pbm rastr
Výstup ve formátu GIF set terminal gif rastr
Výstup ve formátu PNG set terminal png rastr
Formát HPGL (pro plottery) set terminal hpgl vektory
Formát pro tiskárny s PCL set terminal pcl5 vektory
Výstup pro LaTeX set terminal latex vektory
Výstup pro LaTeX s ovladačem pro emTeX set terminal emtex vektory
Výstup ve formátu MetaFONTu (pro TeXovské fonty) set terminal mf vektory

V případě výstupu do externího souboru je zapotřebí provést přesměrování. To se provádí pomocí příkazu set out „soubor či zařízení“. Při požadavku na vytvoření barevného rastrového obrázku typu GIF s grafem, je možné použít tuto trojici příkazů:

set terminal gif
set out "1.gif"
plot "1.txt" 

pro tisk na tiskárnu vybavenou hardwarovým či softwarovým PostScriptem postačí zadat příkazy:

hacking_tip

set terminal post
set out "lpr -Ptiskarna"
plot "1.txt" 

7. Obsah dalšího pokračování tohoto seriálu

V šestnáctém a současně i posledním pokračování tohoto seriálu si ukážeme, jakým způsobem se v gnuplotu pracuje s grafy dvou nezávislých proměnných.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »