diff --git a/src/GS003/01_harjoitus_1.Rmd b/src/GS003/01_harjoitus_1.Rmd new file mode 100644 index 00000000..8eefbb2e --- /dev/null +++ b/src/GS003/01_harjoitus_1.Rmd @@ -0,0 +1,128 @@ +# HARJOITUS 1.1: ASENNUS JA KÄYTTÖÖNOTTO + +**Harjoituksen sisältö** + +Olemassa olevaan GeoServer-palvelimeen otetaan yhteys web-selaimen kautta. Opitaan muokkaamaan palvelimen yleistietoja ja asettamaan ylläpitäjän käyttäjäprofiilitiedot (tunnus ja salasana). + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa kirjautua ylläpitäjänä GeoServeriin ja tuntee yleisellä tasolla sen käyttöliittymän. + +**Arvioitu kesto** + +40 minuuttia. + +## **Valmistautuminen** + +Käynnistä koneessa web-selain. + +Jos käytössä on etäpalvelin, kysy oma ip-osoitteesi ja portti kurssin vetäjältä. + +Mikäli harjoittelet paikallisella GeoServer-asennuksella niin osoite on todennäköisesti + +::: note-box +http://localhost:8080/geoserver +::: + +Edessäsi on GeoServerin käyttöliittymä. Ennen kuin kirjaudut sisään, näet muutamia yleisiä tietoja palvelimesta. + +![](img/harjoitus_1/image1.png) +**Ylhäällä oikealla (1)** pääset kirjautumaan painamalla symbolia. Sen vierestä voit vaihtaa kielen. + +**Ylhäällä keskellä (2)** on kootusti tietoja palvelimen workspaceista ja tasoista. + +**Ylhäällä vasemmalla (3)** on GeoServer-logo ja kun sitä painaa niin pääsee tähän etusivulle. + +**Vasemmalla (4)** on valikko jonka kautta voi nähdä workspacet ja tasot. + +**Keskellä (5)** on palvelimen tukemat karttapalvelut ja niiden versiot. + + +Kirjaudu nyt palvelimeen käyttäen tunnuksena **admin** ja salasanana **gispo**. Kun olet kirjautunut GeoServeriin niin näet myös ylävalikon toiminnot. + +GeoServerin oletusasennuksessa on kaksi käyttäjäprofiilia, **master** ja **admin**. Salasanat on vaihdettu oletuksesta seuraavalla tavalla: + +**master** →  gispogispo + +**admin** →   gispo + +Ero master ja admin-käyttäjien välillä on se, että admin-käyttäjä on tarkoitettu käytettäväksi web-käyttöliittymän hallinnassa. Itse palvelimen asetusten muokkaamiseen käytetään master-käyttäjää, jolla on oikeudet päästä hallitsemaan GeoServerin root-asetuksia sekä suoraan palvelimella että web-käyttöliittymän kautta. Vältä näiden tunnusten käyttöä tuotannollisten palvelimien ylläpidossa. + + +:::hint-box + +Psst! Käyttäjien salasanat voi vaihtaa **Security**-valikon kautta. + +::: + +Suosittelemme, että käytät kurssilla englannikielistä käyttöliittymää. + +## **Ylävalikko** + +Ylävalikon kautta navigoit eri osien välillä. Tässä harjoituksessa keskitytään Geoserver-valikon osioihin. + +- **Welcome** -osio vie sinut aloitusnäkymään. Näet nyt enemmän tietoja, esimerkiksi sivun alalaidassa kyseisen GeoServerin version. + +- **Browse Layers** -osiossa voit esikatsella tasoja. Käy esikatselemassa muutamia tasoja esim. OpenLayers-formaatissa. Käytä nyt muutama minuutti oppiaksesi osion perustoiminnallisuudet. + +- **Demos**-osion kautta voi tutustua muutaman demoon ja esimerkkiin. + +- **About** -osio kertoo tarkemmin GeoServerin asennuksen tietoja ja antaa linkkejä GeoServerin dokumentaatioon. GeoTools- ja GeoWebCache-ohjelmistojen versiotiedot voit tarkistaa myös tältä sivulta. + +### **Palvelutoiminnallisuudet** + +Palaa palvelimen pääsivulle painamalla **GeoServer-logoa**. + +GeoServerille on asennettu oletuksena useita eri rajapintapalveluja. Muita rajapintapalveluita, kuten esimerkiksi WPS-palvelu, voidaan julkaista GeoServeriin laajennusten avulla. Painamalla rajapintapalvelujen versionumeroita, voit tutustua niiden toiminnallisuuksiin (capabilities). + +Tämä osio on nähtävissä vain aloitussivulla. Muista, että aloitussivuun pääset aina painamalla GeoServer-logoa. + +### **Demos** + +Palataan takaisin **Demos**-osiolle. + +Tämän osion alta löytyy muutamia GeoServerin testityökaluja:\ + +![](img/harjoitus_1/image2.png) + +### **Demo requests** + +Kokeile erilaisten kyselyjen (toiminnallisuuksien) tuloksia ja näe, mitä kukin kyselykomento tuottaa. Kokeile **WMS_getMap_OpenLayers.url**-kyselyn toimivuutta valitsemalla kyseinen kysely valikosta ja paina **Show Result** -toimintoa. + +GeoServerin eri toiminnot ja operaatiot muodostuvat URL:n liittyvistä parametreistä. Parametrit ohjaavat GeoServerin rajapintapalveluita: mitä karttatasoa kysytään, miltä alueelta tietoja haetaan tai kuva muodostetaan, vastauksen koordinaattijärjestelmä jne. + +Muokkaa kyselyn parametrejä BBOX, WIDTH ja HEIGHT. Mitä vaikutuksia muutoksilla on vastaukseen? + +### **SRS List ja Reprojection console** + +SRS List -toiminto listaa GeoServerin tukemat koordinaattijärjestelmät. GeoServer sisältää suurimman osan käytettävistä koordinaattijärjestelmistä eli CRS:t (Coordinate Reference System). SRS (Spatial Reference System) on synonyymi CRS:lle. + +Koordinaattijärjestelmien välisiä muunnoksia ja konversioita voit kokeilla Reprojection Console-toiminnon avulla. + +Koordinaatit esitetään WKT-formaatissa (Well Known Text). + +Seuraavassa kuvassa on pistegeometria määritelty WKT-formaatissa EPSG:3067 koordinaattijärjestelmässä **POINT (522800 7615630)**: + +![](img/harjoitus_1/image3.png) + +Voit myös kokeilla ETRS89 / TM35FIN -viivageometriaa. Löydät esimerkin seuraavassa laatikossa. + +::: file-content-box +MULTILINESTRING ((648023.291968740755692 7710356.988362550, 647623.264163991552778 7709773.493117010220885, +647025.100957126589492 7709395.217135690152645, 646919.555495914653875 7709322.892699570395052, 646428.536749321036041 +7708986.160282356664538, 646070.417665179818869 7708777.868454321287572)) +::: + +Kopioi laatikon sisältö ja käytä sitä **Reprojection console** -työkalussa. Kokeile muuntaa se esimerkiksi WGS 84 -järjestelmään. + +![](img/harjoitus_1/image4.png) + +## **WCS request builder** + +Tämän toiminnon avulla voit kokeilla Web Coverage Service -palvelun toimivuutta. + +Lataa **nurc:mosaic**-aineisto PNG-tiedostona Suomen kansallisessa koordinaattijärjestelmässä (**ETRS89/TM35FIN** (EPSG 3067)). Jätä muut asetukset oletusarvoiksi: + +![](img/harjoitus_1/image5.png) + +Paina lopussa **Generate Coverage XML** ja tarkastele tulosta. diff --git a/src/GS003/02_harjoitus_2.Rmd b/src/GS003/02_harjoitus_2.Rmd new file mode 100644 index 00000000..06abf0d3 --- /dev/null +++ b/src/GS003/02_harjoitus_2.Rmd @@ -0,0 +1,226 @@ +# HARJOITUS 1.2: GEOSERVERIN WEB-KÄYTTÖLIITTYMÄ + +**Harjoituksen sisältö** + +Harjoituksessa tutustutaan tarkemmin GeoServerin päätoimintoihin käyttöliittymän kautta. Käydään läpi käyttöliittymän eri osioiden valintoja ja tärkeimpiä ominaisuuksia. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija ymmärtää GeoServerin toimintaa ja eri osioiden hallintatyökaluja. + +**Arvioitu kesto** + +35 minuuttia. + +## **Ylläpitäjän käyttöliittymä** + +Kirjaudu nyt palvelimeen käyttäen tunnuksena **admin** ja salasanana **gispo**. + +Ylävalikosta löytyviä GeoServerin toimintoja on ryhmitelty niiden käyttötarkoituksen mukaan ryhmiin: + ++--------------------------+----------------------------------------------------------------------------+ +| ##### **GeoServer** | Yleistä tietoa GeoServerin asennuksesta ja esikatseluosio | ++--------------------------+----------------------------------------------------------------------------+ +| ##### **Data** | Sisältää aineiston lataamisen, määrittämisen ja kuvaustekniikan toimintoja | ++--------------------------+----------------------------------------------------------------------------+ +| ##### **Services** | Valikossa voi määritellä eri paikkatietopalvelujen ominaisuuksia | ++--------------------------+----------------------------------------------------------------------------+ +| ##### **Server** | Yleisiä asetuksia liittyen palvelimeen | ++--------------------------+----------------------------------------------------------------------------+ +| ##### **Tile Cache** | Karttatiilipalvelujen asetukset | ++--------------------------+----------------------------------------------------------------------------+ +| ##### **Security** | Pääsynhallintaan liittyviä asetuksia | ++--------------------------+----------------------------------------------------------------------------+ +| ##### **Utilities** | Lisätyökaluja | ++--------------------------+----------------------------------------------------------------------------+ + +\ +Näiden lisäksi GeoServerin laajennukset voivat lisätä valikkoja käyttöliittymään. + +![](img/harjoitus_2/image1.png) + +## **GeoServer-valikko** + +Avaa **GeoServer**-valikko ja tutki, mikä GeoServer-versio sinulla on käytössä ja mitkä muiden asennettujen ohjelmistojen versiot ovat. Tämäntyyppiset tiedot ovat tärkeitä palvelimen ongelmia selvitettäessä, tapahtuipa ongelmatilanteiden ratkaiseminen omatoimisesti taikka tukipalvelun avulla. + +## **Data-valikko** + +Tämä valikko sisältää GeoServerin tärkeimtä toimintoja. Aineistot lisätään ja määritellään tämän valikon kautta. + +### **Workspaces** + +**Workspace** on GeoServerin tapa säilyttää ja järjestää viittaukset aineistoihin (**Stores**). Aineistot itse on tallennettu hakemistoon (tai tietokantaan), johon GeoServerillä on käyttöoikeudet. + +Tyypillisesti samassa workspacessa pidetään samanlaisia ja/tai samasta lähteestä saatuja aineistoja. Esimerkiksi organisaation aineistot voidaan järjestää teemojen mukaan workspacen avulla (ymparisto, kaavoitus, vaesto, terveys, jne.). + +Avaa **Workspaces**-osio. Muutama workspace on valmiiksi luotu GeoServerille: + +![](img/harjoitus_2/image13.png) + +Paina **Add new workspace**. + +Luo uusi workspace, jota käytetään jatkossa koulutuksessa, anna sille nimi **helsinki** ja kirjoita **Namespace URI** -kohtaan [**http://gispo.fi/geoserver/helsinki**](http://gispo.fi/geoserver/helsinki): + +![](img/harjoitus_2/image14.png) + +Rastita vielä **Default Workspace**, niin jatkossa juuri luomasi workspace on oletuksena lisättäessä aineistoja ja tasoja GeoServeriin. + +::: hint-box +Psst! URI (Uniform Resource Identifier) on teksti, joka määrittelee tietyn resurssin nimen. Se voi olla nettiosoite tai vaikka suhteellinen hakemisto kovalevystä. Ainoa vaatimus on se, että URI:n arvo on yksilöivä. Voit viitata esimerkiksi osoitteeseen tai kansioon, jossa on säilytetty workspaceen liittyvää dokumentaatiota. +::: + +Paina **Save** ja olet nyt lisännyt uuden workspacen GeoServerille. + +Avaa äsken tekemäsi workspace painamalla **helsinki**-workspace **Workspace Name** -kohdan alta ja huomaa, että lisää valintoja on nyt saatavilla. Palataan niihin myöhemmin säädettäessä workspace-kohtaisia asetuksia. + +![](img/harjoitus_2/image15.png) + +Palaa **Workspace**-näkymään painamalla **Cancel**. + +Workspaceja voidaan poistaa valitsemalla yksi tai useampi workspace ja painamalla **Remove selected workspaces** -toimintoa. + +### **Stores** + +GeoServerillä viitataan aineistolähteisiin **Storesien** avulla. Aineistolähteinä voivat olla yksittäiset tiedostot, tiedostoryhmät, hakemistot, tietokannat tai rajapintapalvelut. + +Oletusasennuksessa on valmiina **Store**-viittauksia esimerkkiaineistoihin: + +![](img/harjoitus_2/image16.png) + +Jokaisen Storen vieressä on kuvake joka viittaa aineiston tyyppiin: + +| Ikoni | Aineistotyyppi | +| :---: | :--- | +| ![raster-file][icon1] | Rasteriaineisto tiedostossa | +| ![vector-file][icon2] | Vektoriaineisto tiedostossa | +| ![vector-db][icon3] | Vektoriaineisto tietokannassa | +| ![wfs][icon4] | Vektori palvelimelta (Web Feature Server) | + +[icon1]: https://docs.geoserver.org/main/en/user/data/webadmin/img/data_stores_type1.png +[icon2]: https://docs.geoserver.org/main/en/user/data/webadmin/img/data_stores_type2.png +[icon3]: https://docs.geoserver.org/main/en/user/data/webadmin/img/data_stores_type3.png +[icon4]: https://docs.geoserver.org/main/en/user/data/webadmin/img/data_stores_type4.png +\ +Tutustu muutamaan storen asetuksiin. Esimerkiksi **sfdem** viittaa rasteriaineistoon (huomaa kuvake) ja **taz_shapes** viittaa vektoriaineistoon. + +Avaa sfdem-store ja taz_shapes-store. Kirjoita muistiinpanoihisi ylös vastaukset seuraaviin kysymyksiin: + +::: hint-box +Mihin aineistoon sfdem-store viittaa? +::: + +::: hint-box +Mihin aineistoon taz_shapes-store viittaa? +::: + +Huomaa, että [**file:data/**](file:data/){.uri} viittaa GeoServerin Data Directory -hakemistoon. Hakemiston sijainti levyjärjestelmässä löytyy **Server → Server Status** -valikon kautta. + +Store **taz_shapes** viittaa hakemistoon, jossa on useampia vektoriaineistoja, tässä tapauksessa .shp-tiedostoja. + +Avaa vielä **taz_shapes**-store ja huomaa, että asetuksissa on rastittu **Enabled**. Se määrittää onko aineisto käytettävissä palvelimelta tai ei. Painamalla **Browse** näet kansiosisällön. + +![](img/harjoitus_2/image18.png) + +Toisin sanoen haluttaessa estää jonkin aineiston käyttöä GeoServerin kautta, ei tarvitse muuta kun jättää rastittamatta **Enabled**-valinta joko layer-, store- tai jopa workspace-valikossa.\ + +Nyt kun olet avannut taz_shapes-storen niin GeoServer näyttää sinulle siihen liittyvän workspacen tiedo (huomaat sen sillä että valikoissa on kansio-kuvakkeet). Jotta pääset takaisin globaalinäkymäät voit painaa GeoServer-logoa ylävasemmalla. + +### **Layers** + +Tasot (**Layers**) määrittelevät yhden aineistolähteen (**Store**) julkaisemisen ominaisuudet, kuten kuvaustekniikan, koordinaattijärjestelmän, metatiedot, palvelun ominaisuudet, karttatiilipalvelun (tile caching) määrittelyt jne. + +Yksi taso vastaa yhtä aineistoa. Aineisto voidaan julkaista useisiin eri tasoihin, esimerkiksi eri kuvaustekniikoilla. + +**Layers**-näkymässä on mahdollista muokata, lisätä tai poistaa tasoja. **Layers**-näkymän taulukossa on nähtävissä muutama taso ja niiden nimet (**Name**). Taulukossa lukee myös tason otsikko (**Title**) ja missä **Store**ssa aineistot sijaitsevat. + +Sarake **Enabled** näyttää tiedon siitä, onko taso käytettävissä palvelimelta vai ei. Huomaa, että käytettävyyden voi määritellä koko storelle (eli aineistolähteelle) tai erikseen tasoille (aineiston näkymille). Myös tason alkuperäinen koordinaattijärjestelmä on ilmoitettu taulukossa, **Native SRS**. + +![](img/harjoitus_2/image20.png) + +Avaa **Manhattan (NY) landmarks** -tason asetusten näkymä painamalla tason otsikkoa (**Title**) tai nimeä (**Name**) sarakkeesta.  + +Tasoon liittyvät asetukset ovat laajat ja siksi ne on jaettu välilehtiin **Data**, **Publishing**, **Dimensions**, **Tile Caching** ja **Security**.  + +Tutustu nyt eri välilehtien asetuksiin saadaksesi kuvan siitä, miten tasot määritellään GeoServerillä. Keskustele niistä kouluttajan kanssa. + +![](img/harjoitus_2/image21.png) + +Tutustumme tarkemmin tasoasetuksiin myöhemmin, kun lisäämme omia aineistoja GeoServerille. + +## **Layer Groups** + +**Layer Groups** -toiminnolla muodostetaan tasoryhmiä. Tasoryhmien avulla on muodostetaan useammasta karttatasosta yhdistettyjä karttatasoja. Tasoryhmiin voidaan lisätä sekä karttatasoja että tasoryhmiä. Muun muassa tasojen järjestystä ja kuvaustekniikoita voidaan määrittää tasoryhmän asetuksissa. + +## **Styles** + +**Styles**-kohdassa määritellään tasoille (**Layers**) kuvaustekniikka, visualisointi.  + +**Styles**-määrittelyjä (kuvaustekniikka) käytetään aina kun tasoja julkaistaan WMS-palveluna. Kuvaustekniikka ei ole riippuvainen tasoista: samaa kuvaustekniikkaa voidaan käyttää useilla tasoilla, ja niillä voi olla useita valinnaisia kuvaustekniikoita. + +Avaa **Styles**-valikko, saat näkyviin listan GeoServerilla olevista kuvaustekniikoista. + +![](img/harjoitus_2/image22.png) + +Avaa **giant_polygon**-tyyli ja tutustu GeoServerin käyttämään kuvaustekniikkaformaattiin. Tasojen kuvaustekniikka määritellään käyttäen Styled Layer Descriptor (SLD) -kieltä. + +![](img/harjoitus_2/image23.png) + +Kuvaustekniikka on hyvin laaja aihe ja siihen tutustutaan erikseen myöhemmin. Kuvaustekniikan toteuttamista voi helpottaa käyttämällä CSS-laajennusta, tätäkin käsitellään myöhemmin koulutuksessa. + +## **Services** + +GeoServerin rajapintapalvelujen (WFS, WMS ja WCS) yleisasetukset määritellään **Services**-valikon kautta ja näihin tutustutaan tarkemmin myöhemmin. + +![](img/harjoitus_2/image24.png) + +## **Server** + +Tässä osiossa voi määritellä palvelimen yleisiä asetuksia. + +### **Contact Information** + +Päivitetään GeoServerin yhteystiedot. Valitse **Server → Contact Information**. + +Täytä omilla tiedoilla ainakin kuvassa merkityt kentät. Nämä tiedot ovat tärkeitä metatietoja GeoServerin ulkopuolisille käyttäjille. + +![](img/harjoitus_2/image3.png) + +### **Server Status** + +Server Status -sivulla on tietoa erilaisista GeoServerin asetuksista ja tilasta. Huomaa esimerkiksi **Data directory** -kansion sijainti. + +Kirjoita omiin muistiinpanoihisi palvelimesi **Data directory**:n sijainti, joka ei välttämättä ole sama kuin kuvassa.\ + +![](img/harjoitus_2/image4.png) + +### **Logs** + +Logs -valinnan kautta voit katsoa palvelimen lokitietoja.  + +![](img/harjoitus_2/image5.png) + + + +### **Global Settings** + +Tämä osio sisältää muun muassa rajapintapalvelujen asetuksia ja lokitietojen asetuksia. + +### **Image Processing** + +Tämä osio sisältää Kuvien (WMS-ja WCS-palvelujen) käsittelyyn liittyviä asetuksia. + +### **Raster Access** +Tämä osio sisältää rasteriaineiston käsittelyyn liittyviä muisti- ja CPU-asetuksia. + + +## **Tile Cache** + +Tämän valikon kautta määritellään karttatiilipalvelujen (tile caching) asetuksia. Karttatiilipalveluihin ja tämän valikon osioihin tutustutaan tarkemmin myöhemmin. + +## **Security** + +Valikko sisältää GeoServerin pääsynhallintaan liittyvät asetukset. Huomaa, että muun muassa pääsynhallinta voidaan määrittää sekä käyttäjä-, ryhmä- ja roolikohtaisesti että aineisto- ja palvelukohtaisesti. Vilkaise nyt **Users**, **Groups, Roles** -valikkoa. + +![](img/harjoitus_2/image26.png) + +Myös pääsynhallintaa käsitellään tarkemmin myöhemmin. diff --git a/src/GS003/03_harjoitus_3.Rmd b/src/GS003/03_harjoitus_3.Rmd new file mode 100644 index 00000000..9b81ce8b --- /dev/null +++ b/src/GS003/03_harjoitus_3.Rmd @@ -0,0 +1,162 @@ +# Harjoitus 1.3: VEKTORIAINEISTOT + +**Harjoituksen sisältö** + +Harjoituksessa lisätään vektoriaineistoja GeoServer-palvelimelle ja muokataan tason asetukset WFS-palvelun julkaisemista varten. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa lisätä vektoriaineistoja ja tehdä välttämättömät asetukset aineiston julkaisemiseksi. + +**Arvioitu kesto** + +40 minuuttia. + +## **Valmistautuminen** + +Kaikki koulutusaineistot ovat valmiiksi GeoServerin aineistohakemisto **/data/koulutus/**-kansiossa. Voit tarkistaa sen sijainnin omassa palvelimessasi **Server Status** -näkymästä. Sijainti on **Data directory** -kentässä ja uusia aineistoja voidaan tarvittaessa lisätä siihen.  + +Harjoituksessa käytettävät aineistot on ladattu käytössä oleville palvelimille. + +![](img/harjoitus_2/image4.png) + +GeoServerin aineistohakemistossa on paikkatiedon lisäksi muita aineistoja, kuten karttatyylejä ja tasoryhmien määrittelyjä. Tarkemmin, kurssin paikkatietoaineistot ovat koulutuskoneen kansiossa:  + +::: note-box +/opt/geoserver/data_dir/koulutus/ +::: + + +## **Vektoriaineiston Storen luominen** + +Koulutuksen aineistosta löytyy muun muassa OpenStreetMapin rakennukset Helsingistä shapefile-formaatissa **buildings.shp**, joka lisätään nyt GeoServeriin. + +Aineiston formaatin ollen **shapefile** täytyy sille luoda **store**, joka on shapefile-formaatin kanssa yhteensopiva. + +Ylävalikosta valitse **Data** ja **Stores**. Paina sitten **Add new Store**. + +Valitse Shapefile aineiston formaatiksi **New data source** -näkymässä, **Vector Data Sources -kohdan** alta. + +![](img/harjoitus_3/image2.png) + +![](img/harjoitus_3/image3.png) + +Seuraavassa näkymässä pääset määrittelemään juuri luomasi storen muut asetukset, kuten nimen ja tiedoston sijainnit. Nimeä ne seuraavalla tavalla: + +- **Valitse** workspaceksi helsinki + +- **Nimeä** aineisto hki_rakennukset + +- **Lisää** halutessasi aineistolle kuvaus, esimerkiksi "Helsingin OpenStreetMap-rakennukset" + +Seuraavaksi määritellään mihin aineistoon (shape-tiedostoon) viitataan tämän store:n kautta. Muokkaa **Connection Parameters**: + +- Paina **Browse...**, ja selaa tiedostoon: **/koulutus/Helsinki.osm.shp/buildings.shp** + +Pidä **Enabled** ruksattuna ja varmista näin tekemäsi store:n (ja siitä luotujen tasojen) käytettävyys GeoServer-palvelimelta. Tätä valintaa voidaan käyttää halutessa piilottaa joitakin aineistoja käyttäjiltä ilman niiden poistamista palvelimelta (ylläpitäjät voivat vielä käyttää niitä). + +Tarkista vielä, että **DBF charset** arvo on **ISO-8859-1**. + +Loput asetukset voidaan pitää oletusarvoina. + +Paina **Save**, niin store tallentuu GeoServerille ja on jatkossa. + +Storen luomisen jälkeen GeoServer olettaa, että halutaan julkaista uusi taso ja siksi uusi taso -näkymä (**New Layer**) aukeaa automaattisesti. + +![](img/harjoitus_3/image4.png) + +![](img/harjoitus_3/image5.png) + +Paina **Publish** tason julkaisemiseksi. + +## **Vektoritason julkaiseminen** + +Luodaan ja julkaistaan nyt uusi taso. Jos et vielä tehnyt niin, paina **Publish** niin pääset muuttamaan julkaistavan tason ominaisuuksia. + +::: hint-box +Psst! Jos ehdit poistumaan edellisestä näkymästä, samaan uuden tason luonti -näkymään pääsee Data → Layers → Add a new layer ja valitsemalla haluttu store (meidän tapauksessa helsinki:hki_rakennukset). +::: + +Tason julkistamisen ominaisuudet ovat jaettu viiteen välilehteen: + +- **Data** + +- **Publishing** + +- **Dimensions** + +- **Tile Caching** + +- **Security** + +## **Data asetukset** + +Keskitytään nyt **Data**-välilehdessä oleviin valintoihin. Määrittele yleistiedot seuraavasti: + +- **Pidä** Enabled ja Advertised rastitettuna + +- **Nimeä** aineisto rakennukset + +- **Otsikoi** Helsingin OpenStreetMap rakennukset + +**Lisää** halutessasi selitystä tasosta ja tasoon liittyviä avainsanoja (keywords). Avainsanojen avulla sekä GeoServerin ylläpitäjien ja käyttäjien on helpompi löytää heille tarpeellisia paikkatietoaineistoja. + +### **Coordinate Reference Systems** + +Selaa alaspäin kunnes löydät **Coordinate Reference Systems** -kohdan. Tässä määritellään tasolle koordinaattijärjestelmä. + +Kun uutta aineistoa tuodaan GeoServeriin, se yrittää tunnistaa alkuperäisen koordinaattijärjestelmän EPSG-koodin. Jos tunnistus ei onnistu, niin **Native SRS** -kentän arvoksi tulee **UNKNOWN** ja sen oikealta puolelta löytyy linkki, josta saa tiedot aineiston alkuperäisen koordinaattijärjestelmän selvittämistä varten. + +Tässä tapauksessa, projektiotiedot tulevat shapefilen aineiston .prj-tiedostosta. Paina **GCS_WGS_1984...** -linkkiä, niin saat auki projektiotiedot. + +![](img/harjoitus_3/image6.png) + +::: hint-box +Psst! Huomaa, että Native SRS tiedot ovat tarkoitettu vaan esikatselua varten. Tiedot tulevat suoraan alkuperäisen aineiston asetuksista, esimerkiksi shapefilen tai PostGIS-tason koordinaattijärjestelmätiedoista. Siksi niitä ei voida muokata GeoServerin selaimessa käyttöliittymän kautta. +::: + +Lisää tarvittaessa **EPSG:4326**-koodi **Declared SRS** -kohtaan. Voit myös etsiä EPSG-koodeja **Find** -toiminnolla. + +Pidä **SRS handling** oletusarvo (**Force declared**) ennallaan. + +![](img/harjoitus_3/image7.png) + +### **Bounding Boxes** + +Täytä alla **Bounding Boxes** -kohdassa tiedot aineiston laajuudesta. + +::: hint-box +Psst! Laajuus olisi mahdollista määritellä automaattisesti, mutta GeoServer vaatii, että tämä asetus tehdään valvotusti. Syy on se, että suurien aineistojen kanssa palvelin voisi jäädä pitkäksi ajaksi laskemaan tasojen laajuutta. Pyyntö voisi jopa epäonnistua, jos palvelimen timeout-asetus ylittyisi ja näin aineiston lisääminen olisi hankala. +::: + +Harjoituksen aineisto on sen verran pieni, että voimme käyttää automaattista määrittelyä. Paina ensin **Native Bounding Box → Compute from data** ja sitten **Lat/Lon Bounding Box → Compute from native bounds**. + +![](img/harjoitus_3/image8.png) + +### **Feature Type Details** + +**Data**-näkymän loppuosasta löytyy **Feature Type Details**, josta nähdään vektoriaineiston ominaisuustiedot ja niiden tyypit. + +Muita julkaisun ominaisuuksia katsotaan vielä myöhemmin. Paina nyt **Save**, ja aineisto on saatavilla GeoServeristä. Tarkista, että uusi taso **rakennukset** on tasojen listalla. + +::: hint-box +Psst! Voit käyttää "etsi"-toimintoa kun haluat suodattaa näkymää. +::: + +Avaa nyt **GeoServer**-ylävalikon kautta **Browse Layers** -näkymä, jolla pääset esikatselemaan **rakennukset**-tasoa. + +::: hint-box +Psst! Huomaa, että Browse Layers-näkymässä (ja myös Layers-näkymässä) tason nimen edessä on laitettu workspacen nimi. Helsingin rakennukset -tason nimi on siksi helsinki:rakennukset. +::: + +OpenLayers esikatselu näyttää tältä: ![](img/harjoitus_3/image10.png) + +Zoomaa karttaan ja paina jonkun rakennuksen päällä niin saat sen ominaisuustiedot näkyviin. + +![](img/harjoitus_3/image11.png) + +## **Helsingin tiestön lisääminen** + +Lisää nyt OpenStreetMap Helsingin tiestö GeoServeriin samalla tavalla kuin edellisessä harjoituksessa. Aineisto löytyy samasta kansiosta kuin **rakennukset**-aineisto, nimellä **gis.osm_roads_free_1.shp**. + +Muista, että ennen kun tason voi lisätä GeoServeriin, sille täytyy luoda sopiva store. Nimeä uusi store **hki_tiesto** ja siitä luotu taso **tiesto** niin tasojen nimet pysyvät samankaltaisina. Muista tarkistaa myös koordinaattijärjestelmä (**EPSG:4326**) ja **DBF charset** (**ISO-8859-1**). diff --git a/src/GS003/04_harjoitus_4.Rmd b/src/GS003/04_harjoitus_4.Rmd new file mode 100644 index 00000000..628ea559 --- /dev/null +++ b/src/GS003/04_harjoitus_4.Rmd @@ -0,0 +1,236 @@ +# HARJOITUS 1.4: KUVAUSTEKNIIKAN PERUSTEET (SLD) + +**Harjoituksen sisältö** + +Harjoituksessa käsitellään eri aineistoille sovellettavia kuvaustekniikoita ja hyödynnetään SLD-tyylejä karttojen visualisointiin. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa hyödyntää peruskuvaustekniikoita GeoServerillä eri aineistojen visualisointiin, käyttäen SLD-kieltä. + +**Arvioitu kesto** + +40 minuuttia. + +## **Valmistautuminen** + +Aiemmissa harjoituksissa on luotu GeoServerin palvelimelle taso **rakennukset**, johon luodaan nyt kuvaustekniikka. + +## **GeoServerin kuvaustekniikka** + +WMS-palvelut visualisoivat karttakuvia GeoServerissä ennalta määritetyillä kuvaustekniikoilla. Tähän asti olemme käyttäneet oletuskuvaustekniikoita aina lisättyämme uusia aineistoja, esimerkiksi kun olemme esikatselleet jotain tasoa. + +Avaa Ylävalikosta **Data** ja sen jälkeen **Layers** ja avaa **rakennukset**-taso. Avaa sitten **Publishing**-välilehti. ![](img/harjoitus_4/image1.png) + +Huomaa, että **Cartography Settings** -kohdasta löytyy **Default Style** -valikko, johon on valittu **polygon**-kuvaustekniikka. + +\ + +![](img/harjoitus_4/image2.png) + +## **SLD-kuvaustekniikka** + +GeoServerin tyylit on määritelty **Styled Layer Descriptorilla** (SLD), joka on XML-kieli. + +Poistu tason näkymästä ja avaa **Data → Styles** näkymä päävalikon alta. Tässä näkymässä määritellään tyylit (kuvaustekniikat), joita WMS-karttatasoissa käytetään. + +Koko GeoServerissä on parikymmentä tyyliä valmiina, jotka tulevat GeoServerin asennuksen mukana. Jos hyppäsit suoraan äskeisestä näkymästä tähän niin suoraan ainoastaan polygon-tyyliä. Katsotaan aluksi, miltä **rakennukset**-tason käyttämä polygon-tyyli näyttää. Avaa se painamalla **polygon**-kohtaa. + +![](img/harjoitus_4/image3.png) + +Huomaa, että **Format** on **SLD**. Tutustu nyt tyylin koodiin, joka muistuttaa verkkosivujen HTML-koodia. Siinä voit tunnistaa muun muassa **\**-ja **\**-tagit, jotka määrittelevät monikulmion täyte- ja rajaviivan värejä. + +Koodin toimivuutta voi kokeilla **Validate**- ja selitteen esikatselua **Preview legend** -toiminnolla. + +Palaa **Styles**-näkymään painamalla **Cancel**. + +## **Uuden tyylin luominen** + +Laaditaan nyt oma kuvaustekniikka Helsingin rakennukset -aineistolle. + +**Data → Styles** näkymässä, lisää uusi SLD-tyyli painamalla **Add a new style**. + +Anna uudelle tyylille nimeksi **oletus_polygoni**. Valitse workspace:ksi **helsinki**. + +Helpottaakseen omien tyylien kirjoittamisen aloittamista, SLD-koodikentän täyttämiseen voidaan valita malliksi GeoServerin oletustyylit tai kopioida olemassa oleva tyyli. + +Valitse **Generate a default style** -alasvetovalikosta **Polygon** ja paina sen jälkeen **Generate...** + +![](img/harjoitus_4/image5.png) + +Voit nyt testata SLD-koodia **Validate-**toiminnolla. Testauksen onnistuminen näkyy vaalensinisellä palkilla. + +![](img/harjoitus_4/image6.png) + +Muutoksia voi otta väliaikaisesti käyttöön painamalla **Apply**. Tätä voi käyttää jos esim. on esikatselemassa tyyliä toisella välilehdellä. Paina nyt kuitenkin **Save**, niin muutokset tallentuvat pysyvästi. + +## **SLD-kuvaustekniikan käyttöönotto** + +Kun uusi tyyli on luotu, voimme käyttää sitä aineistojen visualisoinnissa, esimerkiksi Helsingin rakennuksien esittämiseen kartalla. Sitä varten ilmoitamme GeoServerille, millä tyylillä **rakennukset**-taso on piirrettävä. + +Avaa **rakennukset**-tason editointinäkymä päävalikosta **Data → Layers**.  + +**Publishing** välilehdellä valitse **Default Style** -valikosta uusi **oletus_polygoni**-tyyli. + +Paina sitten **Save**. + +Voit nyt esikatsella **rakennukset**-tasoa, painamalla vasemalla olevaa **rakennukset**-tasoa ja sen jälkeen sivun keskellä olevaa **OpenLayers**-kohtaa. Huomioi, että esikatselu voi olla välillä hidasta riippuen datan määrästä. + +![](img/harjoitus_4/image7.png) +![](img/harjoitus_4/image7b.png) +![](img/harjoitus_4/image7c.png) + +## **SLD Cookbook** + +Kannattaa tutustua SLD-tyylien kirjoittamiseen SLD Cookbook -sivuilta. SLD Cookbook on osa GeoServerin verkkodokumentaatiota ja sisältää erilaisia kuvaustekniikkaesimerkkejä. + +Käytetään seuraavaksi pohjana uuteen tyyliimme esimerkkiä **SLD Cookbookista**.  + +Avaa uudessa selainikkunassa GeoServerin SLD Cookbook alla olevasta linkistä. Tässä harjoituksessa käytetään **[Polygon with styled label](https://docs.geoserver.org/main/en/user/styling/sld/cookbook/polygons/#sld_cookbook_polygons_polygonwithstyledlabel)** -esimerkkiä. + +::: note-box + +::: + +Löydät lyhyen esityksen kuvaustekniikasta ja SLD-koodin. Tutustu lyhyesti koodiin ja yritä löytää tärkeimmät elementit (tagit) kuten monikulmion viivan ja täytön värit sekä tekstien fonttimääritelmät. Voit kirjoittaa muistiinpanoja tärkeimmistä elementeistä ja määritelmistä. + +Esimerkissä **\**-koodissa on määritelty reunaviivan väri ja paksuus. + +**\** sisältää tekstitysominaisuudet eli labelit (kartan kohteiden, esimerkiksi katujen nimet) kuten **\**, joka määrittää tekstejä varten käytettävän kentän/sarakkeen  ominaisuustiedoista. + +Tavanomaisen SLD-kielen lisäksi tämä mallikoodi sisältää kaksi GeoServerin omaa lisäosaa, joita käytetään SLD:ssä tekstien sijoittamiseen. Tunnistat tällaiset kohdat koodista **\**-tageista. + +Etsi SLD-tyylien esimerkkisivuilta **View and download the full "Polygon with styled label" SLD** -linkki. Klikkaa sitä ja tallenna. Riippuen selaimesta niin sinun pitää ehkä avata se uudessa ikkunassa ja tallenna kyseinen sivu .sld-tiedostona, käyttämällä selaimen tallennus (save as / tallenna nimellä) toimintoa. Anna tiedostolle nimi, jonka muistat vielä myöhemmin (esimerkiksi polygon_polygonwithstyledlabel.sld). + +Palaa nyt GeoServerin käyttöliittymään ja mene **Styles**-näkymään ja paina **Add a new style**. Paina koodilaatikon yläpuolelta **Choose file**. Etsi äskettäin ladattu SLD-tyyli levyltä ja paina lopuksi **Upload**. + +![](img/harjoitus_4/image8.png) + +Anna uudelle tyylille nimeksi **rakennukset**. Valitse **helsinki** workspace:ksi. + +::: hint-box +Psst! Huomaa, että jos olit aiemmin määritellyt tyylin nimen ja workspacen, GeoServer mahdollisesti korvaa niitä kun lataat .sld tiedostoa. +::: + +Voit nyt testata SLD-koodia **Validate-**toiminnolla. Voit esikatsella karttamerkkien selitettä painamalla **Preview legend**. + +Muokkaa vielä monikulmion SLD-tyyli seuraavasti: + +- **Täyteväri** (PolygonSymbolizer → FILL) vaaleanpunaiseksi (#FFCC99) + +- **Reunaviivan väri** (PolygonSymbolizer → Stroke) mustaksi (#000000) + +- **Reunaviivan paksuudeksi** 0.5 + +- **Aseta** ensimmäisellä rivillä encoding="UTF-8" + +::: hint-box +Psst! Huomaa, että GeoServerissä desimaalierottimena toimii ainoastaan piste ".". Tämä pätee kaikissa GeoServerin asetuksissa. +::: + +Tallenna sitten muutokset painamalla **Save**. + +Lopuksi vaihda **rakennukset**-tasolle tyyliksi **rakennukset**-tyyli (**Data → Layers → rakennukset → Publishing**). + +![](img/harjoitus_4/image9.png) + +## **SLD-kuvaustekniikan muokkaaminen** + +Lisätään vielä teksteille puskuri (halo), niin tekstiä on helpompi lukea. + +Palaa **Styles**-näkymälle ja valitse taas **rakennukset**-tyyli. Poista SLD-koodista **\**-tagin sisältä: + +::: code-box +\\ +   \#000000\\ +\\ +::: + +ja korvaa sen tilalle: + +::: code-box +\\ +   \3\\ +    \\ +        \#FFFFFF\\ +    \\ +\ +::: + +SLD-koodi ikkunassa näyttää tältä: + +![](img/harjoitus_4/image10.png) + +Paina sitten **Validate**. + +Jos koodin käytössä tulee virheilmoituksia tai se ei toimi, pyydä kouluttajalta apua. Muuten paina **Apply**, niin tyyli tallentuu väliaikaisesti. Voit nyt käydä esikatselemassa, mutta muista vielä lopuksi painaa **Save** jotta muutokset tallentuvat pysyvästi. + +Päivitetty tyyli on heti käytössä palvelimella ja sitä voi esikatsella **rakennukset**-tason avulla. Selainikkunan voit päivittää painamalla **F5**-näppäintä tai **CTRL+R**-näppäimiä. + +![](img/harjoitus_4/image11.png) + +Osoitteessa voi tutustua erilaisiin visualisointityyleihin ja valita  sopivia värejä visualisointiin. + +## **Tiestön tyylin luominen** + +Luo nyt uusi tyyli, jota tullaan käyttämään tiestön kuvaamiseen. + +Samalla tavalla kuin aiemmin, käytä hyväksi GeoServerissa olemassa oleva tyyli. **Tiger_roads**-tyyli sopii tiestön kuvaamiseen. Etsi tiger_roads-tyyli (se ei ole osa helsinki-workspacea) ja mene välilehdell **Layer Preview**. Täälläkin voi esikatsella tyyliä. + +![](img/harjoitus_4/image12.png) + +Kadut on kuvattu leveinä valkoisina kaistoina kun karttaa lähennetään tarpeeksi. Tehdään seuraavaksi samankaltainen tyyli aikaisemmin julkaistua tiesto-aineistoa varten. + +Luo uusi tyyli (**Data → Styles → Add a new style**), nimeä se **tiesto**. Kopioi **simple_roads** tyyli valitsemalla tyyli kohdassa **Copy from existing style** ja painamalla **Copy ...**. + +Etsi **\** kappaleen sisällä oleva **\** ja lisää sen alle: + +::: code-box +\32000\ +::: + +![](img/harjoitus_4/image13.png) + +Paina sitten **Apply**. Pidä välilehti auki ja avaa GeoServeri toiseen välilehteen ja mneen tiesto-tasona asetuksiin. + +Aseta vielä tiesto-tason oletustyyliksi **tiesto-tyyli**. (**Data → Layers → helsinki:tiesto → Publishing**). + +![](img/harjoitus_4/image14.png) + +Paina **Save** ja esikatsele tasoa. Huomaa, että kun lähennät karttaa siten että mittakaava on suurempi kuin 1:32 000, tiet eivät enää tule näkyviin. + +Lisätään nyt tiet myös mittakaavaan 1:32 000 ja suuremmille. Tämä onnistuu luomalla **\**-osio, jossa pienin mittakaava (**\**) on määritelty **32000** arvoksi. + +Voit hyödyntää edellisen sääntö osiota: kopioi koko **\** kappale. Lisää se **\** jälkeen. + +![](img/harjoitus_4/image15.png) + +Muuta vielä: + +::: code-box +\32000\ +::: + +\... tällaiseksi: + +::: code-box +\32000\ +::: + +Vaihda viivan väri harmaaksi **#666666**, otsikoksi **Paksu viiva** ja paksuudeksi **7**. + +![](img/harjoitus_4/image16.png) + +Paina **Apply** ja esikatsele. Huomaa, että nyt kun lähennät karttaikkunaa (suurennat mittakaavaa), niin teiden väri ja paksuus muuttuu. + +Lisätään vielä täyteväri viivoille lisäämällä uusi sääntö. Kopioi edellinen **\** -koodi kappale (jolla on \32000\) ja liitä se kopioidun kappaleen jatkoksi. + +Muuta taas otsikko, väri ja paksuus kuvan mukaisesti: + +![](img/harjoitus_4/image17.png) + +Tämä viiva tulee piirretyksi edellisen paksumman viivan päälle, josta aiheutuu viivareunan efekti. + +Esikatsele lopputulosta: + +![](img/harjoitus_4/image18.png) diff --git a/src/GS003/05_harjoitus_5.Rmd b/src/GS003/05_harjoitus_5.Rmd new file mode 100644 index 00000000..c6122709 --- /dev/null +++ b/src/GS003/05_harjoitus_5.Rmd @@ -0,0 +1,191 @@ +# HARJOITUS 1.5: WMS- JA WCS-PALVELUT + +**Harjoituksen sisältö** + +Harjoituksessa tutustutaan GeoServerin WMS- ja WCS-palveluiden asetuksiin. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija ymmärtää eri karttapalvelujen kokonaisuudet ja osaa muokata niiden perusasetuksia. + +**Arvioitu kesto** + +50 minuuttia. + +## **Valmistautuminen** + +Aiemmissa harjoituksissa on luotu GeoServerin palvelimeen taso **rakennukset**, ja sille tasolle on luotu uusi kuvaustekniikka (rakennukset). + +## **OGC-standardit** + +Kaikista tasoista, jotka on lisätty GeoServeriin, voidaan julkaista erilaisia rajapintapalveluja. + +Yleisin rajapintapalvelu on karttapalvelu **Web Map Service** (**WMS**). GeoServerin avulla voidaan tuottaa WMS-palvelu eri paikkatietoaineistoja ja kuvaustekniikoita hyväksikäyttäen. + +Muita rajapintapalveluja ovat muun muassa **Web Coverage Service** (**WCS**), joka mahdollistaa raakarasteriaineistojen julkaisemisen ja **Web Feature Service** (**WFS**), jota käytetään vektoriaineistojen rajapintapalveluna. + +## **Yleiset karttapalveluasetukset** + +Eri rajapintapalvelujen yleiset asetukset löytyvät päävalikosta **Services**-kohdasta. Näitä yleisiä asetuksia käytetään oletuksena aina, kun aineistoja julkaistaan GeoServerin kautta. + +Käytännössä kaikki aiemmin lisätyt aineistot ovat jo valmiiksi julkaistu rajapintapalveluina, koska kaikki edellä käydyt kolme palvelua (WMS, WCS ja WFS) on aktivoitu koulutuksen GeoServerin oletusasennuksessa. + +Olemme esimerkiksi käyttäneet WMS-karttapalvelua aina kun olemme esikatselleet jotain tasoa selaimella. + +Avaa **Services → Mapping Services**. + +Asetusten sivun alussa löytyy **Service Metadata** -tiedot ja sen jälkeen **Service Control**. + +**Service Metadata**-osiossa määritetään tietoja palvelusta ja sen ylläpitäjästä. + +Tarkista, että **Enable** -valinta on rastitettuna **Service Control**-osiossa. Toiminnon ollessa valittuna GeoServer-palvelin tuottaa WMS-karttapalvelua. Jos tämä valinta ei ole rastittu, mikään aineisto ei ole saatavilla WMS-karttapalveluna. Rajapintapalveluiden saatavuutta voidaan hallita myös workspace-kohtaisesti. + +::: hint-box +Psst! Nämä metadatan asetukset ovat samankaltaiset myös muiden palvelujen kohdalla. +::: + +Määrittele metatietoja WMS-palvelulle. + +![](img/harjoitus_5/image1.png) + +## **WMS** + +**Web Map Services** (**WMS**) -karttapalvelu tuottaa georeferoituja karttakuvia. + +::: hint-box +Psst! Karttakuvat luodaan aina GeoServerin saadessa WMS-pyyntöjä käyttöliittymästä. Lisäksi GeoServer käyttää hyväkseen palvelimen välivarastossa (cache) olemassa olevia karttakuvia. GeoServer saa pyyntöjä aina kun palvelimeen otetaan yhteys, mutta myös silloin kun kartalla zoomataan (vaihdetaan mittakaavaa) tai siirrytään paikasta toiseen. +::: + +Avaa **Services → Mapping** uudelleen ja mene välilehdelle **WMS**. + +Perustietojen lisäksi jokaisella karttapalvelulla on omat asetuksensa, jotka määrittävät karttapalvelun toiminnallisuutta. Näitä on runsaasti WMS-palvelun osalta. Esimerkiksi, kuvien luomiseen voi valita interpoloinnin menetelmäksi **Raster Rendering Options** -kohdassa: **Nearest Neighbor**, **Bilinear** tai **Bicubic**. Oletuksena valittu **Nearest neighbor** -menetelmä on suositeltava valinta rasterien perusvisualisointiin. + +Yksi kiinnostava asetus WMS-palvelulle on vesileimojen käyttäminen. Vesileimoja voidaan lisätä WMS-karttakuviin **Watermark Settings** -kohdasta. Huomaa kuitenkin, että päävalikon WMS-asetuksissa määritellään oletuksia kaikille GeoServerin aineistoille. Tasokohtaisia vesileimoja ei voi määrittää, mutta vesileimoja voidaan määrittää workspace-kohtaisesti. + +Kokeillaan asettaa palvelimen WMS-palvelulle GeoServer-logo vesileimaksi. + +Etsi kohta **Watermark Settings** ja varmista, että **Enable watermark** on rastittuna. + +![](img/harjoitus_5/image2.png) + +Käytä sitten **Browse\...** valitsemaan GeoServerin logo (**/koulutus/GeoServer_100.png)** + +Laita vielä läpinäkyvyyttä **30**:ksi ja sijainniksi **Bottom right**. + +Paina nyt **Save** tallentaaksesi muutokset väliaikaisesti WMS-palveluun. Kaikkien WMS-palvelukartan kautta saatujen karttojen pitäisi nyt sisältää vesileima. + +Avaa **GeoServer → Browse Layers**, ja esikatsele joitakin tasoja. Tarkista, että vesileima tulee näkyviin kaikissa WMS-pyynnöissä. + +Esimerkiksi, **states**-taso näyttää tältä OpenLayers-esikatselunäkymässä: +![](img/harjoitus_5/image3.png) + +Kokeile myös muita karttatasoja ja huomaa, että kaikissa on nähtävissä nyt GeoServer-logon vesileima ja se pysyy siellä karttaa liikuteltaessa. States-aineisto on vektoriaineisto. + +::: hint-box +Mikä GeoServer-palvelu (WMS, WFS tai WCS) käyttää OpenLayersia? Psst! Kannattaa katsoa selaimen osoitteeseen. +::: + +WMS-palvelua hyödyntävä verkkokartta tuotetaan alkuperäisten aineistojen ja tyylien kautta palvelimessa ja välitetään kuvana selaimen kautta. Vektoriaineistoja, rasteriaineistoja ja niiden yhdistelmiä (ryhmätasoina) voidaan välittää WMS-palvelun kautta. + +## **Workspace-kohtaiset palvelimen asetukset** + +Äsken määriteltiin vesileima WMS-karttapalvelulle yleisesti. Asetuksia voi määrittää myös workspace-kohtaisesti. Tätä tehdään workspace-asetusten näkymässä. + +Avaa **Data → Workspaces**. + +Muokataan seuraavaksi **helsinki**-workspacen asetuksia. Valitse se **Workspace Names** -kohdan alta.  + +Rastita **Services** → **Mapping** ja sitten **Settings → Enabled**. + +Voit täyttää nyt tämän workspace-kohtaiset yhteystiedot kuvaamaan Helsingin WMS-karttapalvelua. +![](img/harjoitus_5/image4.png) + +Selaa alaspäin ja paina **Save**. + +Nyt muut GeoServer-palvelimessasi olevat WMS-karttapalvelut sisältävät oletustiedot (määritelty **Server → Contact Information**), kun taas **helsinki**-workspace WMS-palvelun tiedot ovat ne jotka juuri määritettiin. + +Tiedot voit tarkistaa Getcapabilities-pyynnöllä seuraavasti: + +- Helsinki-workspace: [http://xx.xx.xx.xx/geoserver/helsinki/wms?REQUEST=GetCapabilities](http://xx.xx.xx.xx/geoserver/helsinki/wms?REQUEST=Getcapabilities) + +- Tiger-workspace: [http://xx.xx.xx.xx/geoserver/tiger/wms?REQUEST=GetCapabilities](http://xx.xx.xx.xx/geoserver/helsinki/wms?REQUEST=Getcapabilities) + +- GeoServer oletus: [http://xx.xx.xx.xx/geoserver/wms?REQUEST=GetCapabilities](http://xx.xx.xx.xx/geoserver/helsinki/wms?REQUEST=Getcapabilities) + +\ +![](img/harjoitus_5/image5.png) + +## **WMS-karttapalvelun kuvan DPI-resoluutio** + +Karttapalvelimet palauttavat kuvat oletuskuvaresoluutiossa, jonka OGC on määritellyt arvolla **90 DPI** (Dots Per Inch). Tämä dpi-arvo on varsin pieni, mutta sopiva yleisimmissä käyttöyhteyksissä eli tietokoneella tai älypuhelimella. Suurempi karttakuvaresoluutio on tarpeellista esimerkiksi silloin kun työasemapaikkatieto-ohjelmistojen avulla halutaan tuottaa karttatulosteita, joissa on WMS-tasoja. + +::: hint-box +Psst! DPI-arvo on oletusarvoisesti pieni, jotta se ei ylikuormita palvelinta ja mahdollistaa saumattoman karttapalveluiden toimimisen. +::: + +Palveluille on mahdollista määrittää muita kuvaresoluutioita muokkaamalla WMS-karttapyyntöjen parametreja. + +Kokeile nyt esikatsella **poly_landmarks-**tasoa JPEG-kuvana. Löydät tason helposti **Browse Layers** -näkymästä. Valitse **Download sample** alasvetovalikosta WMS:n alta **JPEG**. +![](img/harjoitus_5/image6.png) + +Tuloksena saat karttakuvan JPEG-formaatissa oletusresoluutiolla eli DPI-arvolla 90:\ +![](img/harjoitus_5/image7.png) + +Jos suurennat kuvaa selaimessa (tai tallennat sen kovalevylle ja katselet sitä valokuvien/kuvien katseluohjelmassa) huomaat, että resoluutio on varsin huono. + +![](img/harjoitus_5/image8.png) + +Valitse ja kopioi koko selaimessa oleva osoitteen teksti. Avaa tekstinkäsittelyohjelma ja liimaa teksti siihen. Se on http-pyyntö GeoServerin palvelimellesi ja se muistuttaa seuraavaa tekstiä: + +::: note-box +[http://xx.xx.xx.xx:8080/geoserver/tiger/wms?service=WMS&version=1.1.0&request=GetMap &layers=tiger:poly_landmarks&styles=&bbox=-74.047185,40.679648,-73.90782,40.882078 **&width=528&height=768**&srs=EPSG:4326**&format=image%2Fjpeg**](http://xx.xx.xx.xx:8080/geoserver/tiger/wms?service=WMS&version=1.1.0&request=GetMap%20&layers=tiger:poly_landmarks&styles=&bbox=-74.047185,40.679648,-73.90782,40.882078%20&width=528&height=768&srs=EPSG:4326&format=image%2Fjpeg) +::: + +Tekstiin on korostettu tärkeimmät parametrit koskien WMS-kuvaa. Niistä voi tulkita seuraavaa: + +- **&width** ja **&height**:kuvan koko on 528\*768 pikseliä ja  + +- **&format**: formaatti on JPEG + +- Kuvan resoluutiosta ei ole mitään mainittu pyynnössä, joten käytössä on oletusresoluutio. + +Määrittääksesi kuvaresoluutiota voit lisätä seuraavan parametrin http-pyynnön loppuun: **&format_options=dpi:300**. Lisää se siis WMS-pyyntösi loppuun, ja kopioi ja liimaa se nyt kokonaisuudessaan uuteen selainikkunaan vertailua varten. Painamalla **enter** saat aikaiseksi uuden kuvan: +![](img/harjoitus_5/image9.png) + +Huomataan, että tyylit eivät täsmää aikaisempaan. Se johtuu siitä, että pyynnössä pitää myös muokata kuvan kokoa, joka on laskettu oletusresoluutiolle sopivaksi. Nyt WMS-karttapalvelun tuottama JPEG-kuva on tuotettu kuitenkin jo korkeammalla resoluutiolla kuin aikaisemmin. + +Resoluutiot voidaan tarkistaa halutessaan tallentamalla molemmat kuvat kovalevylle ja vertaamalla niitä zoomaamalla käyttämäsi käyttöjärjestelmän kuvien esikatseluohjelman avulla. + +Kuten sanottu, tyylit eivät nyt näytä oikeilta, koska oletuspyynnössä on kuvan koko laskettu 90 DPI resoluution mukaiseksi. Nyt on siis muokattava **&width-** ja **&height**-parametrien arvot yhteensopivaksi uuteenresoluutioon eli arvolle **300 DPI**. Tämä tapahtuu käyttämällä seuraavaa yhtälöä:\ +![](img/harjoitus_5/image10.png) + +![](img/harjoitus_5/image11.png) + +- uusi_leveys = kuvan_oletusleveys \* (uusi_resoluutio/90) + +- uusi_pituus = kuvan_oletuspituus \* (uusi_resoluutio/90) + +Eli meidän tapauksessamme: + +- uusi_leveys = 528 \* (300/90) → **&width=1760** + +- uusi_pituus = 768 \* (300/90) → **&height=2560** + +Palaa uudelleen tekstinkäsittelyohjelmistoon ja muokkaa wms-pyyntöäsi yllä mainituilla tiedoilla. Kun kokeilet uutta pyyntöä selaimella, kuvan koko, resoluutio ja tyylit ovat oikein: +![](img/harjoitus_5/image12.png) + +WMS-palveluja käyttävät ohjelmistot, kuten QGIS, rakentavat automaattisesti edellä kuvatun prosessin mukaisia pyyntöjä esimerkiksi karttataitossa (tulostekartta), mikä säästää käyttäjän määrittämästä manuaalisesti kuvan kokoa ja resoluution arvoa. + +## **WCS** + +Avaa **Services → Rasters**. + +Totuttuun tapaan voidaan päättää, halutaanko GeoServerin tuottavan raakarasterikarttaa. Se määritellään **Service Control**-osiossa **Enable** -valinnalla. Raakarasteriaineistojen jakamista on hyvä harkita tarkasti. Palvelu voi aiheuttaa suuria datasiirtomääriä verkkoyhteydelle. Lisäksi WCS-palvelun materiaaliksi sopii käytännössä vain luokitellut rasteriaineistot, ilma- ja satelliittikuvat.  + +Siirry **WCS**-välilhedelle nähdäkseen WCS-palvelun asetukset. **Use subsampling** -valinnan aktivointi pienentää käsiteltävää datamäärää, mikä on hyödyllistä käytettäessä aineistoa alkuperäistä pienemmällä resoluutiolla. + +Samalla tavalla, **Overview policy** -toiminto mahdollistaa aineiston yleiskatsausten käytön (näiden ollessa olemassa palvelimessa), mikä osaltansa vähentää siirrettävän tiedon määrää (vähemmän gigatavuja). Oletus on, ettei yleiskatsausta käytetä. Huomaa, että yleiskatsaukset aineistoista on luotava erikseen. + +**Resource Consumption Limits** -valintaa käyttämällä ylläpitäjä voi asettaa resurssien käytölle rajoja raakarasteriaineistoja käsiteltäessä. Raaka-aineistojen koko voi olla hyvinkin suuri (lähtökohtaisesti kymmeniä tai satoja gigatavuja) ja palvelin voi ylikuormittua pyyntöjä käsiteltäessä. + +![](img/harjoitus_5/image13.png) \ No newline at end of file diff --git a/src/GS003/06_harjoitus_6.Rmd b/src/GS003/06_harjoitus_6.Rmd new file mode 100644 index 00000000..d24b404c --- /dev/null +++ b/src/GS003/06_harjoitus_6.Rmd @@ -0,0 +1,179 @@ +# HARJOITUS 1.6: RASTERIAINEISTOT + +**Harjoituksen sisältö** + +Harjoituksessa lisätään rasteriaineistoja GeoServer-palvelimelle ja luodaan ryhmätaso. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa lisätä rasteriaineistoja ja luoda ryhmätason. + +**Arvioitu kesto** + +40 minuuttia. + +## **Valmistautuminen** + +Aiemmissa harjoituksissa on luotu **helsinki**-workspace, joihin on määritelty muun muassa vesileima näkyväksi. + +GeoServerin palvelimen aineistohakemistoon on valmiiksi ladattu muutama rasteriaineisto: korkeusmallikuva ja neljä ilmakuvaa Helsingin alueelta. + +## **Korkeusmallikuvan lisääminen** + +Rasteriaineiston lisääminen GeoServeriin tapahtuu melko samalla tavalla kun aiemmissa harjoituksissa vektoriaineistojen kanssa. Rasteriaineisto on Maanmittauslaitoksen korkeusmalli (ruutukoolla 2 m x 2 m). + +Ensin meidän pitää luoda viitereferenssi aineistoon, jotta GeoServer tietäisi, missä aineisto sijaitsee fyysisesti. Muista, että tämä tapahtuu luomalla storeja. + +Luo uusi store valitsemalla **Data → Stores → Add new Store**. Paina sitten **GeoTIFF** storen formaatiksi. + +Valitse taas **helsinki**-workspace ja nimeä uusi store: **hki_korkeusmalli**. + +Selaa sitten kansioon **/koulutus/korkeusmalli_2m/** ja valitse tiedosto **L4133A_korkeusmalli**.\ +![](img/harjoitus_6/image1.png) + +Paina sitten **Save** ja sen jälkeen **Publish**. + +Kuten aiemmin vektoriaineistojen luomisen yhteydessä, pääset nyt tason editointinäkymään. Tarkista yleisasetukset niin, että sekä **Enabled** että **Advertised** ovat valittuna. + +Laita nimeksi **korkeusmalli** ja otsikoksi **Helsingin korkeusmalli**. + +Aineiston oikea järjestelmä on **ETRS89 / ETRS-TM35FIN** (**EPSG:3067**). Tarkista, että se on oikein. Tarkista myös aineiston laajuuden. Jos olet epävarma laajudesta voit painaa **Compute from data** ja **Compute from native bounds** määrittääksesi aineiston laajuuden. +![](img/harjoitus_6/image3.png) + +Paina sitten **Save**. + +Tarkista, että taso on **Data → Layers**-taulukossa. Voit esikatsella uutta rasteritasoa **Data → Browse Layers** -näkymän kautta.\ +![](img/harjoitus_6/image4.png) + +## **Image Mosaic** + +GeoServerin avulla voi julkaista useita rasteriaineistoja (esim. monta GeoTIFF-tiedostoja) yhtenä WMS-palveluna käyttämällä **Image mosaic** -formaattia. + +Mosaiikin muodostaminen vaatii, että kaikki aineistot ovat samassa kansiossa ja tiedostoformaatissa sekä samassa koordinaattijärjestelmässä. Rasteriformaatti tulee olla tuettuna GeoServerissä. + +Tämä storen tyyppi on tarkoitettu käytettäväksi samantyyppisten rasteriaineistojen kanssa. Esimerkiksi useita ilmakuvia, jotka muodostavat mosaiikin suositellaan julkaistavaksi kyseisellä tavalla välttäen tasokohtaista julkaisemista. + +Palvelimelle on valmiiksi ladattu neljä ilmakuvaa. Aineisto on ladattu Maanmittauslaitoksen aineistopalvelusta. Ne ovat **GeoTIFF-**formaatissa ja **TM35FIN**-**koordinaattijärjestelmässä** (**EPSG:3067**). Kuten nähdään kyseinen aineisto täyttää kaikki mosaiikin muodostamiseen vaatimukset. + +Luo uusi store **helsinki** workspaceen. Valitse formaatiksi **ImageMosaic**. + +Selaa kansioon **/koulutus/nlsfi/Ortokuvat** ja paina **OK**. Täten, kaikki kuvat kansiossa yhdistetään yhteen ja mosaiikkia käsitellään sen jälkeen yhtenä tasona. + +Anna nimeksi **hki_ilmakuvat** ja kirjoita kuvaukseen **Helsingin ilmakuvat**: +![](img/harjoitus_6/image6.png) + +Paina sitten **Save** ja sen jälkeen julkista taso painamalla **Publish**. + +Laita tason nimeksi **ilmakuvat** ja otsikoksi **Helsingin keskustan ilmakuvat**. Lisätiedoksi voit kirjoittaa esimerkiksi **Helsingin ilmakuvat - Maanmittauslaitos**. Tarkista tason editointinäkymästä, että **Enabled** ja **Advertised** ovat rastitettuna. +![](img/harjoitus_6/image7.png) + +Huomaa, että GeoTIFF-tiedoston koordinaattijärjestelmän tiedot (EPSG:3067) ja aineiston laajuus on tässä määrittynyt oikein automaattisesti (oletusarvojen mukaisesti). +![](img/harjoitus_6/image8.png) + +::: hint-box +Psst! Kuten aiemmin mainittiin, GeoServer pyrkii tulkitsemaan alkuperäisen aineiston koordinaattijärjestelmän alkuperäisistä tiedoista, joka tässä tapauksessa oli EPSG-muodossa. +::: + +Paina lopuksi **Save**. + +Tarkista, että uusi taso löytyy **Data → Layers** -näkymältä. Voit myös esikatsella sitä tuttuun tapaan käyttäen **Data → Browse Layers**. + +![](img/harjoitus_6/image9.png) + +## **Layer Groups** + +GeoServerin tasoja ja/tai aiemmin luotuja tasoryhmiä voidaan ryhmittää. Tasoryhmissä voi yhdistää eri vektori- ja rasteriformaatteja. Yksi selkeä käyttötapaus on taustakartan julkaiseminen yhtenä WMS-tasopalveluna.  + +Luodaan nyt tasoryhmä käyttäen aiemmin palvelimelle lisättyjä **ilmakuvat-**, **rakennukset-** ja **tiesto**-tasoja. + +Valitse päävalikosta **Data → Layer Groups** ja sitten **Add new layer group**. + +Laita ryhmätason nimeksi **taustakartta** ja otsikoksi **Helsingin taustakartta**. Lisää **Abstract** kentälle \"Helsingin keskustan taustakartta\". + +::: hint-box +Psst! Joissain selaimissa ääkkösten käyttö ei välttämättä onnistu. Vältä ääkkösten käyttöä tasojen nimissä ja otsikoissa. +::: + +Valitse **helsinki** workspace:ksi. + +Selaa sivua alaspäin, paina **Add Layer...** ja lisää **ilmakuvat**-taso. Taso on nyt lisätty **Layers**-listalle. + +Paina **Generate Bounds** ja huomaa, kuinka tason alkuperäinen koordinaatistojärjestelmä ja laajuus on nyt määritettynä niille kuuluvissa kentissä. Pidetään ryhmätason järjestelmänä **ETRS89 / TM35FIN**. + +![](img/harjoitus_6/image10.png) + +Lisää samalla tavalla Helsingin **rakennukset**- ja **tiesto**-tasot. + +Huomaa, että voit järjestää tasot käyttämällä **Drawing order** -nuolia. Ylempänä oleva (sija 1) piirretään ensimmäiseksi, sen päälle piirretään seuraava taso, ja niin edelleen. Järjestele tasot tämän kuvan mukaan: + +![](img/harjoitus_6/image11.png) + +Rasita **Default Style** tasoille. + +Paina lopulta uudelleen **Generate Bounds** päivittämään ryhmätason laajuustiedot, jotta myös uudet aineistot otetaan huomioon laajuustietojen määrittämisessä. Aina kun lisäät uutta aineistoa tasoryhmään tulisi painaa uudelleen **Generate Bounds** päivittääksesi ryhmätason laajuustiedot. + +Paina vielä **Save**. + +Voit esikatsella ryhmätasoa samalla tavalla kuin muitakin tasoja: + +![](img/harjoitus_6/image12.png) + +### **Ryhmätasojen julkaisemisen muoto** + +Palaa ryhmätason asetuksiin (**Data → Layer Groups → taustakartta**). Selaa alaspäin **Mode**-kohdalle asti. Tässä kohdassa voit valita erilaisia muotoja ryhmätason tasoille: + +- **Single**-ryhmätaso on nähtävissä yhtenä tasona, lähtötasoja ei voi erotella toisistaan + +- **Opaque Container**-ryhmätaso on nähtävissä yhtenä tasona, lähtötasot ei tule esiin edes GetCapabilites-kutsussa + +- **Named Tree** -ryhmätaso on nähtävissä ryhmänä ja yksittäisinä tasoina + +- **Container Tree** -ryhmätaso on nähtävissä ainoastaan yksittäisinä tasoina + +- **Earth Observation Tree** -ryhmätaso on tarkoitettu Earth Observation -aineistoa varten ja se on nähtävissä vain ryhmätason esikatselunäkymässä + +Pidä oletus **Single** valittuna. Se asettaa WMS-tasot näkyville yhtenä tasona. + +Pidä loput asetukset oletuksina ja paina lopuksi **Save**. Ryhmätasoja voidaan esikatsella samalla tavalla kuin muita tasoja. Mene **Layer Preview** -näkymään ja avaa uuden ryhmätason esikatselutila OpenLayers-linkin avulla. Huomaa, että oletuskuvaustekniikka on käytössä. Tämän ryhmätason kuvaustekniikkaa parannetaan vielä myöhemmin. + +Kun olet saanut ryhmätason valmiiksi, voit palata ryhmätason asetuksiin ja kokeilla muita **Mode**-asetuksia. Seuraavat esimerkkikuvat ovat QGISin WMS-palvelun työkalusta. + +**Mode → Single** näyttää tältä: +![](img/harjoitus_6/image13.png) + +Tässä muodossa koko ryhmätaso on aina ladattavissa yhtenä pakettina, eikä sen alitasoista ole saatavilla tietoa erikseen tai niitä voida ladata erikseen. + +Kun taas **Mode →  Named Tree** näyttää tältä: +![](img/harjoitus_6/image14.png) + +Tässä muodossa on mahdollista ladata tasoja yksitellen. + +### **Muiden WMS-palvelujen hyödyntäminen (cascading WMS)** + +Rasteriaineisto voi olla peräisin myös muista WMS-palveluista. Tätä kutsutaan englanniksi **cascading-**tyyppiseksi ratkaisuksi. Käytännössä voidaan ketjuttaa WMS-tasoja toisista palveluista siten, että loppukäyttäjä ei huomaa eroa. Ketjuttaminen mahdollistaa esimerkiksi muiden paikkatietopalvelujen käytön GeoServeristämme. Heikkona puolena on se, että pitkistä ketjutuksista voi syntyä tilanne, jossa palvelun käytettävyys (vasteaika ja saatavuus) huononee oleellisesti. + +Lisää uusi store (**Data → Stores → Add new Store**), valitse **WMS**-listalta. +![](img/harjoitus_6/image15.png) + +Lisätään Helsingin kaupungin avoimet paikkatietopalvelut GeoServeriimme uudeksi WMS-yhteydeksi (remote WMS connection). Kyseinen Helsingin kaupungin palvelu löytyy osoitteesta:  + +::: note-box + [**https://kartta.hel.fi/ws/geoserver/avoindata/wms?REQUEST=Getcapabilities**](https://kartta.hel.fi/ws/geoserver/avoindata/wms?REQUEST=Getcapabilities). +::: + +Täytä tiedot seuraavan kuvan mukaisesti, laita yllä oleva osoite **Capabilities URL** -kenttään: +![](img/harjoitus_6/image16.png) + +Loput asetukset voit jättää oletuksiin. Paina sitten **Save**. + +Seuraavasti voit julkaista tasoja suoraan siitä palvelusta. Julkaise esimerkiksi **Ajantasa_asemakaava**. +![](img/harjoitus_6/image17.png) + +Uuden tason oletusasetukset ovat oikein. Selaa sivun loppuun ja paina **Save**. + +Voit nyt esikatsella uutta tasoa samalla tavalla kuin aiemmin: +![](img/harjoitus_6/image18.png) + +::: hint-box +Psst! Huomaa, että WMS-cascade-tasojen tyyliin tai sisältöön ei voida vaikuttaa, sillä ne ovat Helsingin kaupungin omassa palvelimessa. +::: diff --git a/src/GS003/07_harjoitus_7.Rmd b/src/GS003/07_harjoitus_7.Rmd new file mode 100644 index 00000000..51952c52 --- /dev/null +++ b/src/GS003/07_harjoitus_7.Rmd @@ -0,0 +1,188 @@ +# HARJOITUS 1.7: PAIKKATIETOKANNAT + +**Harjoituksen sisältö** + +Harjoituksessa lisätään aineistoja GeoServeriin tietokantalähteestä. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa yhdistää paikkatietokannan tietoja GeoServeriin ja julkaista sillä niiden aineistoja. + +**Arvioitu kesto** + +30 minuuttia. + +## **Valmistautuminen** + +Palvelinkoneeseen on asennettu PostgreSQL-tietokanta ja sen PostGIS-laajennos. Tietokantaan on ladattu valmiiksi erilaisia vektoriaineistoja. + +## **Yhteyden muodostaminen paikkatietokantaan** + +GeoServer tukee useimpia paikkatietokantoja, kuten PostGIS, ArcSDE, Oracle ja Microsoft SQL Server.  + +PostGIS-paikkatietokannat ovat hyödynnettävissä automaattisesti GeoServerin oletusasennuksien kautta. Muiden tietokantojen tuki asennetaan lisäosien kautta. + +Koulutusympäristöön on asennettu valmiiksi PostGIS-tietokanta, josta löytyy valmiina erilaisia vektoriaineistoja. Aineistot ovat **Helsinki Region Infoshare**, **Jyväskylän yliopiston avoimen datan palveluista** ja **OpenStreetMap -kartta-aineistosta** (geofabrik.de-palvelusta). + +Kuten muiden aineistojen kanssa, jotta aineistoja voidaan käyttää GeoServerissa, niihin on viitattava luomalla sopiva store. + +Luo uusi store (**Data → Stores → Add new Store**). + +Valitse formaatiksi **PostGIS - PostGIS Database**: +![](img/harjoitus_7/image1.png)\ + +::: hint-box +PostGIS vai PostGIS (JNDI)? Kysymys kuuluu, että kuka halutaan että hallinnoi tietokantayhteykseiä (database connection polll). Suora PostGIS-yhteys on nopeampi ja helpompi ottaa käyttöön. PostGIS (JNDI) eli Java Naming and Directory Inteface käytetään kun halutaan, että Tomcat (web application server) on se joka hallinnoi käyttäjätunnusta ja salasanaa. Se on työläämpi ottaa käyttöön mutta silloin tietokantayhtyes ei ole samalla tavalla tiedossa GeoServerillä joka lisää turvallisuutta. +::: + +Valitse **Workspaceksi** tuttu **helsinki**-workspace ja nimeä se **hki_lipas** (**Data Source Name**). + +Täytä sitten PostGIS-tietokannan **Connection Parameters** -yhteysasetukset seuraavasti: + + ++--------------------+---------------+ +| ##### **host** | postgres | ++--------------------+---------------+ +| ##### **port** | 5432 | ++--------------------+---------------+ +| ##### **database** | gs_training | ++--------------------+---------------+ +| ##### **schema** | lipas | ++--------------------+---------------+ +| ##### **user** | postgres | ++--------------------+---------------+ +| ##### **passwd** | gispo | ++--------------------+---------------+ + +Jätä loput asetukset oletuksiin ja paina lopulta **Save**. + +![](img/harjoitus_7/image2.png) + +Samalla tavalla kuin aiemmin, uuden tason luomisen näkymä tulee esiin automaattisesti storen luomisen jälkeen. Näkymässä on listattu kaikki tasot, jotka löytyvät kyseisessä tietokannassa. + +Aineistoon on nyt tehty viittaukset **hki_lipas**-storen kautta ja voit nähdä, mitä aineistoja se sisältää: +![](img/harjoitus_7/image3.png) + +## **PostGIS-tason lisääminen** + +Voit heti julkaista jonkin tason storen luomisen jälkeen painamalla **Publish**. Julkaise nyt **lipas_kaikki_pisteet**-taso. Lipas on Jyväskylän yliopiston hallinnoima valtakunnallinen liikunnan paikkatietojärjestelmä. + +::: hint-box +Psst! Kun myöhemmin haluat julkaista muita tasoja hki_HRI-storesta se onnistuu päävalikosta Data → Layers → Add a new layers ja valitsemalla haluamasi store. +::: + +Tason editoinnin näkymässä, tason nimi ja otsikko on automaattisesti täytettynä. Pidä oletusnimet ja lisää halutessasi lisätietoja **Abstract**-kentälle. + +Tarkista, että **Enabled** ja **Advertised** ovat rastittuina. + +Kuten olet varmasti nyt huomannut, uuden tason asetukset ovat samanlaiset, oli kyseessä sitten shapefile-formaatti tai PostGIS-tietokanta. + +Katso vielä **Coordinate Reference Systems** -osiosta, että GeoServer on tunnistanut koordinaattijärjestelmäksi **EPSG:3067**. PostGIS ja GeoServer käyttävät EPSG-koodeja määrittääkseen koordinaattijärjestelmän, joten useimmiten koordinaattijärjestelmä tulee oikein määritellyksi automaattisesti. + +Ennen kuin tallennetaan tason julkaisuasetukset, määritä tason **Bounding Boxes** samalla tavalla kuin aikaisemmin lisäämiesi aineistojen kanssa. + +![](img/harjoitus_7/image5.png) + +Paina lopulta **Save**. Voit esikatsella uutta tasoa **Browse Layers**-näkymän kautta. + +![](img/harjoitus_7/image6.png) + +## **Monikulmio- ja viiva-tason lisääminen PostGIS-tietokannasta** + +Lisää samalla tavalla vielä kaksi uutta tasoa PostGIS-tietokannasta: + +- **lipas_kaikki_alueet** + +- **lipas_kaikki_reitit** + +Muista, että saat tasoja lisättyä **Data → Layers → Add new layer** -toiminnon kautta ja valitsemalla **helsinki:hki_lipas** lähteeksi. Esikatsele sen jälkeen näitä tasoja tarkistaen niiden toimivuus. + +::: hint-box +Mitkä vaiheet ovat pakollisia tason julkaisemiseksi? Mitkä ovat tasojen koordinaattijärjestelmät ja niiden laajuudet? +::: + +## **Tasojen oletustyylien asettaminen** + +Vaihda lopuksi oletustyylit kyseisille tasoille käyttäen GeoServerin valmiita kuvaustekniikoita (default styles). Voit vaihtaa tason tyylin päävalikosta **Data → Layers** ja avaamalla haluamasi tason. Tyylit määritellään **Publish** välilehdestä. Käytä seuraavia tyylejä: + ++--------------------------+-----------------+ +| ##### **Taso** | ##### **Tyyli** | ++==========================+=================+ +| **lipas_kaikki_pisteet** | burg | ++--------------------------+-----------------+ +| **lipas_kaikki_alueet** | giant_polygon | ++--------------------------+-----------------+ +| **lipas_kaikki_reitit** | simple_roads | ++--------------------------+-----------------+ +\ +Voit vielä lopuksi tehdä aineistoista ryhmätason. Kuvassa on yhdistetty Helsingin taustakartta-ryhmätaso ja lipas-aineistot yhteen karttapalvelutasoon: +![](img/harjoitus_7/image7.png) + +## **SQL-näkymät** + +GeoServerin avulla voidaan hyödyntää myös SQL-kyselyitä paikkatietoaineistojen jakamisessa. + +Lisätään toistamiseen **lipas_kaikki_reitit**-aineisto uutena tasona palvelimelle. Tavoite on luoda taso, jossa ainoastaan Helsingissä olevat aineistot näytetään. + +Annan nimeksi **lipas_kaikki_reitit_helsinki**. + +::: hint-box +Psst! SQL-näkymä voitaisiin luoda myös suoraan tietokantaan ja käyttää tuota näkymää (view) tason luomiseen. +::: + +SQL-kyselyjen tekemiseksi on hyvä ensin selvittää tietokannan taulujen kenttien nimet ja tyypit. Voit tarkistaa aineiston kentät avaamalla tason **Data → Layers**-näkymästä; kentät on lueteltu **Data**-välilehden lopussa. Aineiston ominaisuustietojen tyyppejä pääset tarkastelemaan parhaiten käyttäen muita ohjelmistoja kuten pgAdmin tai QGIS. Voit  myös tutustua niihin [Lipas-sivuilta](https://www.jyu.fi/fi/liikunta/yhteistyo/lipas-liikunnan-paikkatietojarjestelma). Attribuuttitiedot GeoServerissä näyttävät tältä: + +![](img/harjoitus_7/image8.png) + +Aineiston tarkastelemisen jälkeen voidaan päätellä, että paikkakuntatieto löytyy **kunta_nimi**-kentästä. + +Avaa **Data → Layers → Add new layer** ja valitse **helsinki:hki_lipas**. Painamalla **Configure new SQL view...** pääset määrittelemään SQL-kielellä, mitkä tiedot haluat julkaista kyseisestä aineistosta. + +![](img/harjoitus_7/image9.png) + +Määrittele näkymän nimeksi **lipas_kaikki_reitit_helsinki**. Kirjoita **SQL statement** -kenttään seuraavaa SQL-lauseke: + +::: code-box +SELECT \* + +FROM lipas.lipas_kaikki_reitit + +WHERE kunta_nimi = \'Helsinki\' +::: + +Rastita **Guess geometry type and srid** -toiminto ja paina sitten **Refresh**. +![](img/harjoitus_7/image10.png) +\ + +Tarkista sitten, että **geom**-nimisen kentän kohdalla **SRID** on määritelty oikean EPSG-tunnuksen mukaisesti (meidän tapauksessamme tunnus on **3067**). + +![](img/harjoitus_7/image11.png) + +Paina lopulta **Save**. + +Määrittele seuraavaksi tason laajuus (Bounding Box) ja paina **Save**. Voit esikatsella Helsingin liikuntareitit -tasoa nyt. +![](img/harjoitus_7/image12.png) + +## **Paikkatietotoiminnot SQL-näkymien kautta** + +Voit käyttää SQL-lausekkeissa myös paikkatietofunktioita. Käytetään esimerkkinä vyöhyke-funktiota (ST_Buffer). + +Avaa tason **lipas_kaikki_reitit_helsinki** uudelleen (**Data → Layers**). + +Selaa **Data** välilehdessä sivun loppuun. Sieltä löytyy **Edit sql view** -toiminto. + +Päivitä **SQL statement** seuraavaksi: + +::: code-box +SELECT ST_Union (ST_Buffer ( geom, 10 )) + +FROM lipas.lipas_kaikki_reitit + +WHERE kunta_nimi = \'Helsinki\' +::: + +Päivitä taas attribuuttien formaatti ja paina sitten **OK** ja **Save**. +![](img/harjoitus_7/image13.png) + +Esikatselussa huomaat, että reitit-aineisto on nyt puskuroitu 10 metrillä: +![](img/harjoitus_7/image14.png) diff --git a/src/GS003/08_harjoitus_8.Rmd b/src/GS003/08_harjoitus_8.Rmd new file mode 100644 index 00000000..e37ebc6e --- /dev/null +++ b/src/GS003/08_harjoitus_8.Rmd @@ -0,0 +1,227 @@ +# HARJOITUS 2.1: WFS-PALVELUT + +**Harjoituksen sisältö** + +Harjoituksessa tutustutaan WFS-karttapalvelujen asetuksiin ja toiminnallisuuteen. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa muuttaa WFS-karttapalvelujen julkaisuasetukset ja käyttää WFS-T-karttapalvelua. + +**Arvioitu kesto** + +30 minuuttia. + +## **Valmistautuminen** + +GeoServeriin on lisätty storeja, joissa on vektoriaineistoja. + +## **OGC-standardit** + +Aiemmin tarkasteltiin Open Geospatial Consortiumin (OGC) määrittämien WMS- ja WCS-rajapintapalvelujen asetuksia. Web Feature Service (WFS) -latauspalvelu on myös OGC:n määrittelemä rajapintapalvelu, jonka kautta vektoriaineistoja ja niiden ominaisuustietoja voi ladata edelleen hyödynnettäväksi. + +## **WFS-asetukset** + +WFS-palveluiden avulla voidaan käsitellä vektoriaineistoja sekä niiden geometria- ja attribuuttitietoja verkon yli. + +Avaa **Services → Vectors**. Heti näkymän alusta löytyy palvelun yleistiedot (**Service Metadata**). + +WFS-kohtaiset asetukset löytyvät WFS-välilehdellä, josta määritellään, miten WFS-kohtaisia palvelupyyntöjä käsitellään ja miten niihin vastataan. + +## **Tietomäärä pyynnöissä** + +**Maximum number of features** ja **Maximum number of features for preview** estävät liian ison aineistomäärän lataamisen. Jos latauspyyntö sisältää paljon kohteita (features), niin vain osa niistä latautuu. + +Toiminnon testaamista varten määrittele kyseisten ominaisuuksien arvoksi **1**. + +Paina sitten **Save**. + +Mene sen jälkeen **tiesto**-tasolle ja valite esikatselutavaksi GML. + +::: hint-box +Miten äsken määritellyt ominaisuudet ovat vaikuttaneet lopputulokseen? Miksi? +::: + + +![](img/harjoitus_8/image1.png) + +Palvelun vastaus (xml-muodossa) sisältää nyt vain yhden geometrian, kuten olimme määritelleet asetuksissa. + +Palaa vielä **Services → Vectors → WFS**-näkymään ja rastita **Return bounding box with every feature**. Esikatsele taas samaa tasoa ja vertaile sitä aiemman palvelupyynnön tulokseen. Tietyissä sovelluksissa voi olla tarpeen saada tiedot jokaisen paikkatietokohteen sijainnin rajauksesta (bounding box). + +Tarkista tiesto-tason GML-esikatselusta, että GeoServerin lähettämä vastaus sisältää nyt myös geometrioita rajaavan suorakaiteen. + +![](img/harjoitus_8/image3.png) + +Palaa uudelleen **Services → Vectors → WFS** -näkymään ja palauttaa oletuksiin **Maximum number of features**: **1 000 000** ja **Maximum number of features for preview**: **50**. + +Paina **Save**. + +Voit vielä tarkistaa, että nyt GML-esikatselussa tulee vain 50:n kohteen geometriat, vaikka tiesto-tasolla on paljon enemmän paikkatietokohteita. + +::: hint-box +Psst! "Maximum number of features for preview" on tärkeä asetus, jolla saa rajattua WFS-pyyntöjen tuloksien määrää tason esikatselutilassa. Saat helposti tietokoneen jumiin, jos kaikki tason tiedot tulevat vastauksena yhtä aikaa. +::: + +## **Useiden koordinaattijärjestelmien määrittäminen** + +GeoServer pystyy projisoimaan aineistoa ja palvelemaan sitä kaikissa yli 5000:ssa tuetussa koordinaattijärjestelmässä. On kuitenkin huomioitava, että palvelimessa käytössä olevat järjestelmät on määriteltävä käsin sen sijaan, että kaikki 5000+ koordinaattijärjestelmää tulisi ilmoitettua GetCapabilities-pyynnön kautta. + +::: hint-box +Psst! Tämä asetus vaikuttaa vain GetCapabilities-pyynnön sisältöön; projisointi onnistuu edelleen käyttämällä mitä tahansa client-järjestelmää (esim. paikkatieto-ohjelmistoa). +::: + +Määrittele **Extra SRS codes for WFS capabilities generation** -kentässä käytetyt koordinaattijärjestelmät pilkulla eroteltuina: **4326, 3047, 3067**. + +::: hint-box +Mitkä koordinaattijärjestelmät ovat kyseessä? +::: + +Voit tarkistaa nyt, että WFS-toiminnallisuuksien dokumentissa yllä mainitut koordinaattijärjestelmät ovat ilmoitettuna. + +Paina **GeoServer-logoa** vasemmasta yläkulmasta (palaa siis web-käyttöliittymän etusivulle) ja sen jälkeen valitse sivun keskeltä **WFS** → **1.1.0** tai **2.0.0**. + +Etsimällä "**EPSG**" sivulta löydät EPSG-koodit WFS-palvelun dokumentaatiosta (xml-formaatissa): + +Huomaa, ettei tämä asetus toimi WFS 1.0.0, sillä siinä ilmoitetaan EPSG-koodit vain tasokohtaisesti. + +![](img/harjoitus_8/image4.png) + +## **Service Levels** + +GeoServerissa voidaan julkaista vektoriaineisto editoitavaksi **WFS-T (Transactional)** -palvelun avulla. Mene taas **Services → Vectors → WFS** -näkymään. + +**Service Level** -valinnat määrittävät millaisen palvelutason WFS tarjoaa. + ++-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ##### **Basic** | Tarjoaa perustoiminnallisuuden: GetCapabilities, DescribeFeatureType ja GetFeature | ++-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ##### **Transactional** | (WFS-T) perustoiminnallisuuden lisäksi transaktio-toiminto on käytettävissä | ++-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ##### **Complete** | Lisää vielä LockFeature-tuen, joka estää paikkatietokohteiden (feature) muokkauksen sen jälkeen, kun toinen käyttäjä on jo aloittanut kohteiden muokkauksen | ++-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +Jätä asetus **Complete**-tasolle. GeoServer sallii oletuksena aineistojen muokkauksen vain admin-ryhmään kuuluville käyttäjille, eivätkä tunnistautumattomat käyttäjät pysty muokkaamaan aineistoja. **Basic**-tasoa voidaan käyttää, jos halutaan estää täysin aineistojen muokkaus WFS-T:n avulla. + +## **GML-asetukset** + +**GML** on XML:ään perustuva kieli, jonka OGC on määrittänyt. GML käytetään mallintamaan geometriaelementtejä. GeoServer tuottaa vektoriaineistoja GML-muotoisina. + +Erilaiset WFS-palvelun versiot palauttavat oletuksena eri GML-formaatit: + ++---------------------+---------------+ +| ##### **WFS 1.0.0** | GML 2 | ++---------------------+---------------+ +| ##### **WFS 1.1.0** | GML 3 | ++---------------------+---------------+ +| ##### **WFS 2.0.0** | GML 3.2 | ++---------------------+---------------+ + +Jokaiselle GML-formaatille on mahdollista määritellä koordinaattijärjestelmäformaatti, joka tulee ottaa huomioon GML-tuloksessa. Tämä on tärkeä etenkin kun kehitetään WFS-palveluita hyödyntäviä sovelluksia. + +### EPSG-koodit ja koordinaattien formaatit GML-dokumentissa + ++--------------------------+-----------------------------------------------------------------------------------------------------------+ +| **EPSG Code** | srsName="EPSG:4326" | +| | | +| | Koordinaatit formaatissa: longitude/latitude (x/y) | ++--------------------------+-----------------------------------------------------------------------------------------------------------+ +| **OGC HTTP URL** | srsName="[http://www.opengis.net/gml/srs/epsg.xml#4326"](http://www.opengis.net/gml/srs/epsg.xml#4326%22) | +| | | +| | Koordinaatit formaatissa: longitude/latitude (x/y) | ++--------------------------+-----------------------------------------------------------------------------------------------------------+ +| **OGC Experimental URN** | srsName="" | ++--------------------------+-----------------------------------------------------------------------------------------------------------+ +| **OGC URN** | srsName="::4326" | +| | | +| | Koordinaatit formaatissa: latitude/longitude (y/x) | ++--------------------------+-----------------------------------------------------------------------------------------------------------+ +| **OGC HTTP URI** | srsName="[http://www.opengis.net/def/crs/EPSG/0/4326"](http://www.opengis.net/def/crs/EPSG/0/4326%22) | ++--------------------------+-----------------------------------------------------------------------------------------------------------+ + +\ +Esimerkiksi **Lipas-alueet**-taso GML2-esikatselusta (EPSG-koodi muodossa OGC HTTP URL): + +![](img/harjoitus_8/image5.png) + +## **Workspace-kohtaiset asetukset** + +Samalla tavalla kun WMS-palvelujen kanssa, myös WFS-asetuksia on mahdollista määrittää workspace-kohtaisesti. Esimerkiksi WFS-T-toiminnallisuus voidaan sallia vain yhden workspacen kautta ja pääsynhallinnan avulla sallia vain tunnistetut käyttäjät muokkaamaan aineistoja. + +Avaa **Data → Workspaces** ja valitse sitten **helsinki**-workspace. Rastita sitten **Services → Vectors** ja paina sen jälkeen **Save**. + +![](img/harjoitus_8/image6.png) + +Varmista, että **Service Level** asetus on **Complete** menemällä **Services → Vectors** ja valitse workspaceksi **helsinki** ja siirry **WFS**-välilehdelle. + +![](img/harjoitus_8/image7.png) + +Paina sen jälkeen **Save**. + +Workspace-kohtaisilla asetuksilla voidaan esim. rajoittaa, missä koordinaattijärjestelmissä workspacen aineistot ovat saatavilla, tai poistaa WFS-T-palvelu tietyn workspacen aineistoista. + +## **Editointi WFS-T-palvelun kautta** + +Yksi tärkeä ominaisuus WFS-palveluissa on mahdollisuus editoida vektoriaineistoja suoraan palvelimessa. WFS-T-palvelulle voidaan myös asettaa lukitus-mahdollisuus (**Complete** service-taso), joka on tarpeellinen tehdessä editointeja jaetulla aineistolla. + +Editointia voidaan tehdä eri client-ohjelmistojen avulla, esimerkiksi QGISin tai web-sovellusten kautta. + +Helpoiten saadaan testattua editointi-toiminnallisuutta QGIS-paikkatietojärjestelmän avulla. + +Tarvitaan ensin WFS-palvelun osoite. Saat tämän esimerkiksi GeoServerin etusivulta, jonne pääset klikkaamalla vasemman yläkulman GeoServer-logoa. Klikkaa hiiren oikealla painikkeella etusivun **Service Capabilities** -paneelin WFS-kohdan alla olevaa linkkiä **1.1.0**. + +Osoite on muotoa: + +::: note-box + +::: + +Huomaa, että yllä olevassa laatikossa **ip.osoite** kohdassa kuuluu olla oma ip-osoitteesi. + +GeoServerin oletusasetuksilla workspace-kohtaisen osoitteen saa lisäämällä osoitteeseen workspacen URI:n. Esimerkiksi helsinki-workspacen osoite olisi: + +::: note-box + +::: + +Avaa QGIS-ohjelmisto. Lisää uusi WFS-palvelu ja liitä URL-kenttään äsken kopioimasi osoite. Syötä myös käyttäjänimi (admin) sekä salasana (gispo). + +![](img/harjoitus_8/image8.png) + +Tarvittaessa, seuraa kouluttajan ohjeita **QGISin** käytössä. Yhdistä QGIS omaan WFS-karttapalveluusi, lisää karttaprojektiin **lipas_kaikki_reitit**-taso, jonka lisäsit aiemmin toisessa harjoituksessa. Editoi joitakin paikkatietokohteita ja tallenna muutokset. + +Katso jälkeenpäin GeoServerin esikatselusta editoimasi kohteet ja totea, että muutokset ovat todella tallentuneet palvelimeen. + +## **Muiden WFS-palvelujen hyödyntäminen (cascading WFS)** + +Samalla tavalla kun on mahdollista edelleen julkaista WMS-palveluja (**cascading WMS**), niin GeoServerin avulla voidaan myös edelleen julkaista WFS-palveluja (**cascading WFS**). + +Lisää uusi store (**Data → Stores → Add new Store**), valitse **Web Feature Server (NG)** listalta. + +![](img/harjoitus_8/image9.png) + +Lisätään Lipas-WFS palvelu. Nimeä uusi vektori-store **lipas_WFS** ja laittaa **WFS GetCapabilities URL** arvoksi: + +::: note-box + +::: + +![](img/harjoitus_8/image10.png) + +Loput asetukset voit jättää oletusarvoihin. Paina sitten **Save**. + +Uusi store sisältää karttatasoja, jotka on jaettu alkuperäisessä  WFS-palvelimessa: + +![](img/harjoitus_8/image11.png) + +Voit julkaista tasoja suoraan palvelusta. Julkaise esimerkiksi **lipas_lipas_101_lahipuisto**. Paina oikealta **Publish** ja määrittele **Bounding  Boxes** totutulla tavalla. + +Paina sitten **Save**. + +Voit nyt esikatsella uutta tasoa (oletusnimeksi tulee **lipas_101_lahipuisto**). + +![](img/harjoitus_8/image12.png) + +::: hint-box +Psst! Mikäli aineisto on suuri ja maksimilatausaika on määritelty alhaiseksi, GeoServer voi tuottaa error-koodin kun aineistoa ei saada ladattua määrättyyn aikaan mennessä. Muuta tätä aikaa, mikäli aineisto on suuri. +::: diff --git a/src/GS003/09_harjoitus_9.Rmd b/src/GS003/09_harjoitus_9.Rmd new file mode 100644 index 00000000..bdc8c1fe --- /dev/null +++ b/src/GS003/09_harjoitus_9.Rmd @@ -0,0 +1,374 @@ +# HARJOITUS 2.2: KARTTATIILIPALVELUT + +**Harjoituksen sisältö** + +Harjoituksessa hyödynnetään GeoServerissa olevia karttatiilien ja ruudukkojen luontityökaluja. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija pystyy luomaan ja säätämään omia karttatiilipalveluja. + +**Arvioitu kesto** + +40 minuuttia. + +## **Valmistautuminen** + +GeoServerin palvelimeen on ladattu ja julkaistu useita rasteri- ja vektoriaineistoja. GeoServerin oletusasennukseen on myös integroitu GeoWebCache, jota GeoServer käyttää karttatiilien luomiseen ja hallitsemiseen. + +## **Tile Cache** + +GeoServer sisältää **Tile**-palvelut osana **GeoWebCache-**palvelua. + +Avaa **Tile Cache → Tile Cache Settings** ja tarkista, mitkä karttatiilipalvelut ovat käytössä GeoServerin asennuksessasi. + +![](img/harjoitus_9/image1.png) + +Oletuksena aktivoituna ovat **WMS-C**- (WMS Tile Caching) ja **TMS** (Tile Map Service)-karttatiilipalvelut. + +Avaa vielä **Tile Cache → Tile Layers** ja huomaa myös, että listalta löytyy jo tiilitettyjä tasoja.\ +![](img/harjoitus_9/image2.png) + +## **GeoWebCache-käyttöliittymä** + +Yleisimmät karttatiilipalvelun asetukset määritellään jo tutuiksi tulleissa eri GeoServerin web-käyttöliittymän asetusosissa. Tarkemmin karttatiilipalvelun toimintaa hallitaan GeoWebCache:n käyttöliittymästä. + +Palaa **Tile Cache → Tile Cache Settings** -näkymään ja avaa vielä GeoWebCache:n oma asetusnäkymä painamalla **Go to the embedded GeoWebCache home page**.\ +![](img/harjoitus_9/image3.png) + +GeoWebCache on oletuksena asennettu GeoServeriin ja se hallitsee kaikkia karttatiiliä, joita jaetaan karttatiilipalvelujen kautta. + +Avaa lista demoista **A list of all the layers and automatic demos**. + +Kaikilla GeoServerin tasoilla on oma karttatiilipalvelu käytössä:\ +![](img/harjoitus_9/image4.png) + +Avaa **helsinki:ilmakuvat** OpenLayers **png**-esikatselu linkkiä ja selaile karttaa. Huomaat, miten eri karttatiilet lasketaan ja piirretään näyttöön sen mukaan kun palvelin saa niitä luotua. + +Testaa, että karttatiilet pysyvät palvelimessa uudelleen käytettävänä siirtymällä kartalla paikasta toiseen suurin piirtein samalla kartta-alueella. Karttatiilet tulevat nyt nopeammin näkyviin, koska GeoServerin ei enää tarvitse piirtää niitä, vaan se vain välittää palvelimella jo valmiina olevia karttatiiliä. + +## **Tasokohtaiset asetukset** + +Tasojen karttatiiliasetukset löytyvät päävalikon **Tile Cache → Tile Layers** -näkymän kautta. Avaa **helsinki:ilmakuvat_keskusta** -tason ominaisuudet painamalla tason linkkiä taulusta. \ + +![](img/harjoitus_9/image5.png) + +Pysy **Tile Caching**-välilehdellä. + +![](img/harjoitus_9/image6.png) + +Oletusasetukset on määritelty siten, että kaikille uusille tasoille luodaan automaattisesti karttatiilet. Myös sekä jpeg- että png-kuvatiilet ovat oletuksena aktivoituna. + +Selaa vielä tämän näkymän alaosaan, josta selviää **Available gridsets** -osiosta, mitkä karttatiiliruudukot ovat oletuksena käytössä:\ + +![](img/harjoitus_9/image7.png) + +::: hint-box +Psst! Tasokohtaisia tiilitysasetuksia pääset myös muokkaamaan Data → Layers näkymän kautta valitsemalla tason ja sen jälkeen Tile Cache -välilehden. +::: + +## **Karttatiilien esiluominen** + +Palaa **Tile Cache → Tile Layers** näkymään. Tältä sivulta voit myös esikatsella tiilitettyjä karttapalveluita. + +**Preview**-sarakkeen alta valikosta löytyy kyseiselle tasolle saatavilla olevat tiilitysvaihtoehdot. Saat aikaiseksi saman esikatselun kuin käyttäessäsi GeoWebCache:n näkymää. + +**Actions**-sarakkeen alta löytyy vielä linkki **Seed/Truncate**, jolla pääse GeoWebCache:n tasojen esitiilittämiseen. **Empty**-linkillä tyhjennetään kaikki tasolle aiemmin tallennetut karttatiilet. +\ + +![](img/harjoitus_9/image8.png) + +Valitse nyt **Seed/Truncate** -linkki Helsingin ilmakuvien tason karttatiilien luomista varten. + +**GeoWebCache**-näkymässä sivun yläosasta löytyy yleisiä toimintoja ja lista meneillään olevista tehtävistä. Karttatiilien luominen on hyvin resurssi-intensiivinen ja kohtuullisia levytiloja vaativa prosessi. On myös tyypillistä, että tehtävien eli karttatiilien luominen voi kestää hyvinkin pitkään (tunteja, jopa päiviä). Sivun tiedot päivitetään käyttämällä **Refresh list** -toimintoa. + +![](img/harjoitus_9/image9.png) + +Tiilien luomista varten asetukset löytyvät sivun alapuolelta **Create a new task** -osiosta. + ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Number of tasks to use** | Määrittää kuinka monta prosessia käynnistetään karttatiilien luomiseksi | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Type of operation** | Tiilien luomisen toimintotapa | +| | | +| | Reseed: luodaan kaikki karttatiilet, korvataan vanhat karttatiilet | +| | | +| | Seed: luodaan vain puuttuvat karttatiilet | +| | | +| | Truncate: poistetaan karttatiilet palvelimesta | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Grid set** | Määrittää mille karttatiiliruudukolle luodaan karttatiilet | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Format** | Karttakuvan tiedostomuoto (esim. jpg, png) | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Zoom start, Zoom stop** | Mittakaavavälit luotavalle karttatiilille (mittakaavat on määritelty karttatiiliruudukoille) | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Modifiable Parameters** | Kertoo mitkä WMS-pyynnön muuttujat vaikuttavat tiilien ulkoasuun ja pitää siksi huomioida | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Bounding box** | Sijainnin suorakaiderajaus, jolta karttatiilet luodaan | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Tile failure retries** | Montako kertaa yritetään luoda tiiltä jos sen luonti epäonnistuu | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Pause before retry (ms)** | Epäonnistuneen luonnin jälkeen pidettävä tauko (millisekunneissa) | ++-----------------------------------+----------------------------------------------------------------------------------------------+ +| **Total failures before aborting**| Montako epäonnistunutta tiilenluontikertaa, kunnes lopetetaan koko prosessi | ++-----------------------------------+----------------------------------------------------------------------------------------------+ + +\ + +::: hint-box +Psst! Jos et tiedä mitä voit laittaa kohtaan "Number of tasks to use", 2\*tietokoneen_suorittimien_lukumäärä on hyvä lähtökohta kokeilulle. +::: + +Aseta **Number of tasks to use** -asetukseksi 1. + +Aseta tehtävän asetukset seuraavalla tavalla, huomaa erityisesti **Zoom stop** -asetus. Laita arvoksi 15, niin lasketaan valmiiksi mittakaavatasot 0-15: + +Paina **Submit**. + +GeoServer (GeoWebCache) muodostaa nyt karttatiilet valitusta palvelusta mittakaavatasoista 0–15. + +Muodostamisen ajan käyttöliittymässä voi seurata karttatiilien valmistumista: + +Paina **Refresh list** -linkkiä, kun haluat päivittää tiedot laskennan etenemisestä. + +Karttatiiliruudukon määritelmän ja tason sijainnin rajauksen mukaan mittakaavoille 0–15 tarvitaan 1 090 karttatiiltä. Kuvien piirtäminen kestää etäpalvelimella näillä asetuksilla minuutin verran.\ +![](img/harjoitus_9/image10.png) + +Huomaa, että karttatiilet luodaan oletuksena tason sijainnin rajauksen mukaisesti (ellei sitä toisin ole määritelty). ![](img/harjoitus_9/image11.png) + +Laske vielä karttatiilet **tasolle 16** (määrittele zoomaus aloitus- ja lopputasoksi 16). Päivitä laskennan aikana sivua ja tutki karttatiilien luomisen etenemistä.\ + +Jos erehdyt määrittelemään vääriä asetuksia, voit pysäyttää laskennan käyttämällä **Kill all Tasks...** -toimintoa painamalla **Submit**-näppäintä. ![](img/harjoitus_9/image13.png) + +Taso 16 vastaa mittakaavaa 1:4 261. Kaikkiaan sen tiilittämiseen tarvitaan 1 936 karttatiiliä, joiden luomiseen menee noin pari minuuttia. Laske vielä taso 17 samalla tavalla ja kirjoita muistiinpanot prosessorimäärästä, karttatiilistä sekä ajasta. + +Taso 17 näyttää kartalla tältä: ![](img/harjoitus_9/image14.png) + +## **Karttatiilien kovalevyn käyttö** + +Edellisessä kohdassa laskettiin karttatiiliä vain yhdelle tasolle (**ilmakuvat**), vain yhteen karttatiiliruudukkoon (**EPSG:4326**) ja vain yhdelle kuvaformaatille (**png**). Tämän karttatason mittakaavatason 16 karttatiilet vievät noin 240 MB levytilaa. + +Karttatasoilla saattaa olla useita karttatiilipalveluita riippuen siitä, kuinka monta karttatiiliruudukkoa ja kuvatyyppiä on tason tiilitysasetuksissa määritelty. + +Karttatiilien luominen kannattaa suunnitella etukäteen ja toteuttaa huolellisesti. Yksi tärkeä asetus karttatiilipalveluissa on karttatiilien kovalevyn käytön tarkkaileminen ja hallinnointi. Siihen on GeoWebCache:ssa erilaisia asetuksia ja osaa niistä voi säätää suoraan GeoServeristä. + +Avaa **Tile Cache → Tile Layers** -näkymästä **ilmakuvat** tason asetukset. Avaa vielä **Tile Cache** välilehti. + +Sivun asetuksista voit tarkistaa, että sekä **jpeg**- että **png**-kuvaformaatit ovat rastitettuina. Sen lisäksi on määritelty **neljä eri karttatiiliruudukkoa** käytettäväksi. Tämä tarkoittaa sitä, että tämä taso on saatavilla kahdeksalla eri karttatiilitasolla. Äskettäin laskettiin valmiiksi karttatiiliä vain yhdelle. + +Voit tarkistaa kovalevyn käytön tasokohtaisesti **Tile Cache → Tile Layers** -näkymästä. Huomaa miten paljon tilaa ilmakuva-tason tiilet käyttävät. + +![](img/harjoitus_9/image15.png) + +On hyvä tapa määrittää, kuinka paljon kovalevytilaa karttatiilipalvelulla on lupa käyttää. Oletuksena GeoWebCache voi käyttää koko palvelimen levytilan. + +Palataan hetkeksi takaisin näkymään **Tile Cache** → **Disk Quota**. Märittele vielä **Maximum tile cache size** -arvoksi **2 GiB**. + +![](img/harjoitus_9/image16.png) + +::: hint-box +Psst! Käyttöliittymässä käytetään lyhenteitä GiB ja GB. Niiden ero tulee siitä, että GiB (Gibibyte) on binäärijärjestelmä monikerta kun GB (Gigabyte) on kymmenjärjestelmä monikerta: GiB on 1024 MiB ja tavuina 230 tavua → eli 1 073 741 824 tavua GB on 1000 MB ja tavuina 109 tavua → eli 1 000 000 000 tavua +::: + +Huomaa vielä, että **When enforcing disk quota limits, remove tiles that are** -asetus tarkoittaa se, että GeoServer/GeoWebCache ei lopeta karttatiilien luomista, vaikka määritelty maksimi-karttatiilien levykäyttö saavutettaisiin. Sallitun levytilan täyttymisen jälkeen palvelin alkaa poistaa automaattisesti joko vähiten tai pitkän aikaan käyttämättä olleita karttatiiliä. Karttatiiliä tuotetaan siis jatkuvasti, vaikka maksimilevytilan raja tulisikin täyteen. + +Paina **Save** ja palaa **Tile Cache → Tile Layers -**näkymään. + +Karttatiilet tulevat myös tallennetuksi automaattisesti aina karttatiilipalvelun luodessa karttatiiliä. Kokeile esikatsella eri tasojen karttatiilitasoja (**Tile Cache** → **Tile Layers** → **Preview**). Esimerkiksi rakennukset- ja taustakartta-tasoja.\ + +![](img/harjoitus_9/image18.png) + +Voidaan todeta, että sen jälkeen myös niiden kohdalla **Disk Used** -sarakkeessa on tietoa levytilan käytöstä. Niiden levytilan käyttö kasvaa sitä mukaan kun eri kartta-alueita ja tiilitasoja käytetään.\ + +![](img/harjoitus_9/image19.png) + +Edellisessä kohdassa laskettiin muutamia karttatiilitasoja. + +Tasojen käyttämää levytilaa voidaan myös vapauttaa. Voit halutessasi tyhjentää tietyn tason kaikki karttatiilet käyttäen tason **Empty**-linkkiä. Kokeile tyhjentää **rakennukset**-tason karttatiilet. + +On mahdollista myös tyhjentää ainoastaan tietyn mittakaavan karttatiilet GeoWebCache-käyttöliittymällä:  + +- Avaa **ilmakuvat**-tason oikealta **Seed/Truncate** ja valitse mittakaavataso 17 alku- ja loppumittakaavaksi. + +- Valitse sama **Grid Set** asetus kuin aikaisemmin, eli EPSG:4326. + +- Valitse toiminnoksi **Truncate** ja lopuksi **Submit**. Tällä tavalla poistat palvelimesta kaikki mittakaavatason 17 karttatiiltä. + +- Voit tarkistaa **Tile Layers** -näkymästä **ilmakuvat_keskusta**-tason nykyisen kovalevyn käytön. + + +## **Karttatiiliruudukot** + +GeoServerin oletusasennuksesta löytyy valmiiksi määriteltyjä karttatiiliruudukoita. + +Avaa **Tile Cache → Gridsets** ja paina sitten **EPSG:4326** -nimistä ruudukkoa. ![](img/harjoitus_9/image20.png) + +Karttatiiliruudukon määritelmiin kuuluvat muun muassa: + ++---------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| **Coordinate Reference System** | Koordinaattijärjestelmä, jolla ruudukot määritellään.\ | ++---------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| **Gridset bounds** | Karttalehtijaon sijainnin rajaus, jolla määritetään, mille sijainnille karttatiiliruudukko on rajattu. | ++---------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| **Tile Matrix Set** | Karttatiilimalli (kuvapyramidi), jolla määritellään karttatiiliruudukolle valitut mittakaavat. Tämä voidaan määritellä joko resoluution tai mittakaavan mukaan. | ++---------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +\ +Karttatiiliruudukkoja voi myös laatia omiin tarpeisiin. + +## **Lisätehtävä 1** +Jos jää aikaa, voit jatkaa tästä tekemällä lisätehtäviä. + +## **JHS180 mukainen karttatiiliruudukko** + +[JHS180](https://geoforum.fi/jhs-180-paikkatiedon-sisaltopalvelut/) suosituksesta löytyy muun muassa suositus kansallisesta karttatiiliruudukosta, joka perustuu OGC-standardien määrittelyihin. Suosituksesta löytyy muun muassa seuraavat määritelmät: + ++------------------------------+----------------------------------------------------------------------------------------------------------------+ +| **Rajapintastandardi** | OGC:n WMTS standardi | ++------------------------------+----------------------------------------------------------------------------------------------------------------+ +| **Koordinaattijärjestelmä** | ETRS89 / TM35-FIN (EPSG:3067) | ++------------------------------+----------------------------------------------------------------------------------------------------------------+ +| **Käytettävä origo** | Itä -548 576 m, pohjoinen 8 388 608 m (tiilien ankkuripiste on vasen yläkulma) | ++------------------------------+----------------------------------------------------------------------------------------------------------------+ +| **Resoluutiotasot** | 0.25 m, 0.5 m, 1 m, 2 m, 4 m, 8 m, 16 m, 32 m, 64 m, 128 m, 256 m, 512 m, 1 024 m, 2 048 m, 4 096 m ja 8 192 m | ++------------------------------+----------------------------------------------------------------------------------------------------------------+ +| **Tiilien koko pikseleissä** | 256\*256 pikseliä | ++------------------------------+----------------------------------------------------------------------------------------------------------------+ +| **Kuvaformaatti** | Image/png | ++------------------------------+----------------------------------------------------------------------------------------------------------------+ + +\ +Näillä määritelmillä, karkeimmalla resoluutiotasolla (taso 0) on vain yksi tiilikuva kooltaan 2 097 152 m \* 2 097 152 m (8 192\*8 192 pikseliä). Tämän kuvatiilen pohjois-etelä -suuntainen keskilinja sijoittuu projektiokaistan keskimeridiaanille (27 astetta). Ja tiilen pohjoisreunan pohjoiskoordinaatti on tiilen koon monikerta päiväntasaajalta laskien (4 \* 2 097 152). + +Seuraava taso (taso 1) muodostuu jakamalla 0-tason ainoa tiili neljään yhtä suureen osaan. Tämä tapa jatkuu tasolle 15 asti. + +## **Uuden karttatiiliruudukon luominen** + +Luodaan GeoServerin palvelimelle uusi karttatiiliruudukko JHS180-suositusten mukaisesti. + +Valitse **Tile Cache → Gridset** päävalikosta ja luo uusi karttatiiliruudukko painamalla **Create a new gridset**. + +Anna nimeksi esimerkiksi **JHS180_EPSG3067** ja kirjoita lyhyt määritelmä ruudukosta. + +Valitse **Coordinate Reference System** -arvoksi **EPSG 3067**. + +Määrittele vielä ruudukon kattavuus aiemmin mainituilla JHS180-suosituksella, eli karkeamman tason kattavuus. + +Suosituksissa sanotaan, että vasemman yläkulman koordinaatit (origo) ovat itäkoordinaatti **-548 576** m ja pohjoiskoordinaatti **8 388 608** m. Näitä vastaavat **Min X** ja **Max Y** koordinaatit. Karkeamman tason koko metreinä on 2097152 \* 2097152 joten: + +**Max X** on sitten: -548576 + 2097152 = **1 548 576** m + +**Min Y** on sitten: 8388608 -- 2097152 = **6 291 456** m + + ++------------------------------------------------------------------------------------------+ +| **  JHS180-mukaisen ruudukon kattavuus** | ++=================================================+========================================+ +| Min X: -548 576 m | Max X: 1 548 576 m | ++-------------------------------------------------+----------------------------------------+ +| Min Y: 6 291 456 m | Max Y: 8 388 608 m | ++-------------------------------------------------+----------------------------------------+ + + +Pidä tiilen pikselikoko oletuksena, **256** px. + +Samat arvot löydät suosituksen mukaisesta tiiliruudukosta. + +![](img/harjoitus_9/image21.png) + +Täytä karttatiiliruudukon tiedot yllä mainituilla arvoilla, kuitenkin siten, että et käytä välilyöntejä koordinaatteja kirjoittaessasi. + +![](img/harjoitus_9/image23.png) + +Määritellään seuraavaksi resoluutiotasot. **Tile Matrix Set** -asetuksissa voit määritellä JHS180 suosituksessa mainitut **resoluutiotasot**. + +Pidä valittuna **Resolutions**. Aloita määrittelemällä taso 0. + +Paina **Add zoom level** ja huomaa, että **Pixel Size** -arvoksi tulee **8 192** (pilkut numeroissa johtuvat englannin numeroformaatista), joka on juuri suosituksessa mainittu kuvatiilen pikselikoko tällä tasolla. Huomaa, että **Scale**-arvo täyttyy myös automaattisesti. + +Kun painat uudelleen **Add zoom level**, uuden tason **Pixel Size** -arvo tulee automaattisesti puoleksi edellisen tason pikselikoosta, eli 4 096. + +Luo loput resoluutiotasot (tasolle 15 asti) samalla tavalla. + +![](img/harjoitus_9/image23.png) + +**Tiles**-kohdalta voit tarkista kuinka monta tiiliä tarvitaan jokaisen tason puolelta. + +Paina lopulta **Save**, niin uusi JHS180-karttatiiliruudukko on käytettävissä palvelimessasi.\ + + +## **Suomen OpenStreetMap-tiestö** + +Äskettäin luomamme karttatiiliruudukko soveltuu aineistoihin, jotka kattavat koko Suomen. + +Aiemmissa harjoituksissa olet harjoitellut aineistojen käyttöönottoa GeoServerissä. Samasta jo aiemmin käytetystä **gs_training** PostGIS-tietokannasta löytyy Suomen OpenStreetMap-tiestöaineisto **EPSG 3067** -koordinaattijärjestelmässä. + +Luo uusi taso tiestö-aineistosta. Suomen aineisto löytyy tietokannasta **suomi**-skeemasta nimellä **osm_tiet**. + +::: hint-box +Psst! Muista, että ensin luodaan store, josta luodaan viittaus edellä mainittuun skeemaan ja sen jälkeen voidaan lisätä tasoja GeoServer-palvelimelle. +::: + +**Voit katsoa ohjeita paikkatietokannat-harjoituksesta**, jossa lisättiin aineistoja GeoServeriin PostGIS-paikkatietokannasta. Voit myös kysyä tarvittaessa kouluttajalta. + +Luo workspace, anna sille nimeksi **suomi**. + +Luo PostGIS-store, laita se suomi-workspaceen ja nimeä se **suomi_tiesto_3067**:ksi (tietokannan nimi on **gs_training**, skeema on **suomi**, käyttäjä on **postgres** ja salasana on **gispo**). + +Kyseisestä storesta löytyy aineisto nimellä **osm_tiet**. Luo siitä uusi GeoServer-taso, nimeä se **suomi_tiesto_3067**:ksi (täytä myös muut tarvittavat tiedot). Muista asettaa koordinaattijärjestelmä (EPSG:3067). Jos alkuperäinen aineisto ei ole EPSG:3067 niin valitse **Reproject native to declared** ja valitse **Declared SRS**-kohtaan EPSG:3067. + +Tarpeen vaatiessa, voit katsoa mallia alla olevasta kuvakaappauksesta PostGIS-storen luomisesta: + +![](img/harjoitus_9/image24.png) + +## **Oman karttatiiliruudukon käyttöönotto** + +Avaa **Tile Cache → Tile Layers** ja paina **suomi_tiesto_3067** -linkkiä. Avaa vielä **Tile Cache** -välilehti ja selaa sivun loppuun, josta löydät karttatiiliruudukko-asetukset. + +Valitse **Available gridsets** -osion alta **Add grid subset** -kohdasta uusi **JHS180_EPSG3067**-karttatiiliruudukko. Paina vielä **plus**-kuvaketta. + +Tarvittaessa voit määritellä, mitkä zoomaus-tasot ovat käytettävissä tai mitkä tallentuvat välimuistiin käyttäen **Published**- ja **Cached zoom levels -asetuksia**. + +Paina lopulta **Save**. + +![](img/harjoitus_9/image26.png) + +Esikatsele nyt **suomi_tiesto_3067** uudella karttatiiliruudukolla näkymästä **Tile Cache → Tile Layers**. Valitse tiestötason Preview-kohdalla **JHS180_EPSG3067/png**.  + +Esikatselussa on käytössä luomasi EPSG:3067 karttatiiliruudukko (alla huomataan, kuinka käytössä ovat karttatiilet, ja kuinka Etelä-Suomea kohti mentäessä karttatiilet eivät vielä ole ehtineet renderöitymään ruudulle näytönkaappauksen ottohetkeen mennessä): + +![](img/harjoitus_9/image27.png) + +\ + + +## **Lisätehtävä 2** + +## **Paikallisen karttatiiliruudukon muodostaminen** + +GeoServeriin voidaan muodostaa tarpeiden mukaan hyvin monenlaisia karttatiiliruudukoita. Joissakin tapauksissa voi olla tarpeen luoda oma paikallinen karttatiiliruudukko. Paikallisen karttatiiliruudukko voi olla tarpeellinen, jos karttatiilipalvelulle on runsaasti käyttöä ja aineistoihin halutaan yhdistää paikallisessa koordinaattijärjestelmässä olevia tietoaineistoja (vektori- tai rasteriaineistoja). + +Luodaan nyt Helsingin alueelle oma karttatiiliruudukko, joka käyttää **ETRS89 / GK25FIN (EPSG:3879)** -koordinaattijärjestelmää. Määritellään resoluutiotasot mittakaavan mukaan. + +Palaa **Tile Cache** → **Gridsets**-näkymään ja paina **Create a new gridset** -toimintoa. Nimeä uusi karttatiiliruudukko **HKI-GK25FIN**-nimellä. + +Määrittele sitten **EPSG:3879** koordinaattijärjestelmäksi ja paina vielä **Compute from maximum extent of CRS**. + +Rastita **Tile Matrix Set** -osion alta **Scale denominators** ja lisää kymmenen karttatiilitasoa (tasot 0-9) painamalla **Add zoom level**. ![](img/harjoitus_9/image29.png) + +Määrittele ensimmäisen tason mittakaava käsin **Scale**-sarakkeessa, anna arvoksi 500 000. Määrittele loput: 250 000, 100 000, 50 000, 20 000, 10 000, 5 000, 2 500, 1 000 ja 500 . ![](img/harjoitus_9/image30.png) + +Paina lopussa **Save**. + +Ota uusi karttatiiliruudukko käyttöön samalla tavalla kuin aiemmin. + +Avaa **Tile Cache → Tile Layers → taustakartta → Tile Caching**. Ryhmätasoillekin voidaan määritellä karttaruudukkoja. + +Selaa sivun loppuun, josta löydät tiilitysasetukset. + +Valitse **Available gridsets** -osion alta **Add grid subset** -kohdasta uusi **HKI_GK25FIN**-karttatiiliruudukko. Paina vielä **plus**-kuvaketta. + +Paina lopulta **Save**. + +Näkymästä **Tile Cache → Tile Layers**, esikatsele nyt **taustakartta** uudella karttatiiliruudukolla (valitse esimerkiksi **HKI_GK25FIN/jpeg**). Huomaa, että mittakaavatasoja on vain kymmenen ja ne vastaavat karttatiiliruudukon asetuksissa määriteltyjä mittakaavoja. ![](img/harjoitus_9/image31.png) diff --git a/src/GS003/10_harjoitus_10.Rmd b/src/GS003/10_harjoitus_10.Rmd new file mode 100644 index 00000000..d8dd936e --- /dev/null +++ b/src/GS003/10_harjoitus_10.Rmd @@ -0,0 +1,126 @@ +# HARJOITUS 2.3: ARKKITEHTUURI JA KÄYTTÖÖNOTTO + +**Harjoituksen sisältö** + +Harjoituksessa tutustutaan tarkemmin GeoServerin lokeihin ja erilaisiin asetuksiin, jotka vaikuttavat suorituskykyyn. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa arvioida GeoServerin suorituskykyä lokien avulla ja tunnistaa erilaisia ongelmatilanteita. + +**Arvioitu kesto** + +30 minuuttia. + +## **Valmistautuminen** + +GeoServerin palvelimeen on ladattu erilaisia aineistoja ja GeoServerin **Monitor**-lisäosa on asennettu palvelimeen. + +## **Yleistiedot GeoServerin palvelimesta** + +Avaa GeoServerin yläpalkista **Server → Server Status**. ![](img/harjoitus_10/image1.png) + +**Locks**-toiminnon kautta voit tarkistaa olemassa olevat WFS-T -aineistojen lukitukset. Voit tarvittaessa vapauttaa lukitukset painamalla **Free locks**. + +**Connections**-tieto viittaa siihen, kuinka monta vektori-storea on saatavilla.  + +Voit tarkistaa ja vapauttaa GeoServerin käyttämää muistia **Memory Usage** kohdalla. + +## **Image Processing -asetukset** + +Tarkista vielä, mitkä Java-asetukset ovat voimassa palvelimessa kuvien käsittelyä varten. Lähtökohtaisesti ei ole kovin tehokasta käsitellä kuvia kokonaisina tietokoneen muistissa. Yleisesti käytetty tapa on jakaa kuvat pienempiin osiin (tiilet/tiles) ja tehdä mahdolliset kuvaoperaattorit pienempiin kuviin. JAI-asetukset (määritelty **Image Processing** -asetuksissa) hallinnoivat näiden menetelmien toimintaa. + +JAI (Java Advanced Imaging) asetuksia voit muokata **Server → Image Processing** -näkymän kautta. + +![](img/harjoitus_10/image2.png) + + +Asetusten muutokset näkyvät **Server → Server Status** -sivulla. + +## **Raster Access** + +Toisin kuin WMS-kuvapyynnöt, WCS-pyynnöt tuottavat mahdollisesti hyvin suuria kuvatiedostoja. Näissä tapauksissa kuvien lataaminen muistista ei ole hyvä ratkaisu, vaan olisi suotavaa käyttää väliaikaista tiedostoa. + +**Raster Access** -asetukset määräävät GeoServerin toimintaa kuvapyyntöjen saapuessa. Saat asetukset näkyviin **Settings → Raster Access** -näkymästä: + ++-----------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| | #### **Raster Access -asetukset** | ++-----------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| **ImageIO cache memory threshold (KB)** | Tätä arvoa suurempia kuvapyyntöjä käsitellään väliaikasta tiedosto-cachea käyttäen, muuten käytetään välimuisti-cachea | ++-----------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| **CPU Use** | Rasterikuvien luomisen aikana käytettävien prosessien asetukset | ++-----------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + +![](img/harjoitus_10/image3.png) + +**Server → Server Status** -näkymän lopussa on palvelimeen liittyviä asetuksia: + ++-------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | #### **Muut palvelimen asetukset** | ++-------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| **Update Sequence** | Kertoo kuinka monta kertaa muutoksia on tehty palvelimen asetuksiin. | ++-------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| **Resource cache** | GeoServer tallentaa store-yhteydet, ulkopuoliset kuvat sekä kohdetyyppien, fonttien ja koordinaattijärjestelmien määritykset. Tarvittaessa cachen tiedot voidaan poistaa. | ++-------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| **Configuration and catalog** | Kaikki GeoServer-konfiguraation asetukset on ladattu palvelimen muistiin. Tarvittaessa niitä voidaan ladata uudelleen. | ++-------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + +## **GeoServerin loki** + +Tutustutaan GeoServerin lokin sisältöön ja sen asetuksiin suorittamalla pyyntöjä palvelimeen käyttäen esikatselun toimintoa. + +Avaa erilliseen selainikkunaan jonkun tason esikatselu (**Layer Preview**) ja pidä karttaikkuna auki. + +Avaa eri selainikkunaan **Settings → Global** näkymää oikeasta palkista. + +Avaa kolmanteen selainikkunaan GeoServerin lokien esikatselu (**GeoServer Logs**). + +![](img/harjoitus_10/image4.png) + +Tutustu asetuksiin lukemalla lisää tietoa asetuksista GeoServerin [manuaalista](https://docs.geoserver.org/main/en/user/configuration/globalsettings/). Avaa manuaali erilliseen selainikkunaan. + +Suorita nyt muutamia toimintoja (zoom in, zoom out, pan) karttaikkunassa.  + +Tarkista tuliko lokeihin mitään: paina **Refresh**, niin uusimmat rivit lokitiedostosta tuleva näkyville. Voit myös vähentää lokirivien määrää (**Maximum console lines**). + +GeoServerin asennuksessa on valmiina ladattuja erilaisia loki-profiileja, jotka raportoivat erilaisia tietoja lokiin. + +![](img/harjoitus_10/image5.png) + +Voit nyt vaihtaa erilaisia lokitus profiileja ja havainnoida toimintojen aiheuttamat vaikutukset lokitiedostoihin. Kokeile asettaa profiiliksi VERBOSE_LOGGING ja liikuttele karttaa. Päivitä tämän jälkeen loki (refresh) ja katso mitä rivejä lokiin on ilmestynyt. + +## **REST API:n toimintaa** + +GeoServerin palvelimen asetuksia on mahdollista hallita myös REST-rajapinnan kautta. + +Voit tarkistaa REST-rajapinnan kautta saatavilla olevat aineistot kirjoittamalla seuraavan osoitteen web-selaimeen (käytä oman palvelimesi ip-osoitetta): + +::: note-box + +::: + +Etsi tiedot edellisissä harjoituksissa luoduista karttatasoista. + +![](img/harjoitus_10/image6.png) + +::: hint-box +Kuinka toteuttaisit uuden kuvaustekniikan tallentamisen GeoServeriin REST-rajapinnan avulla? +::: + +::: hint-box +Psst! Tutustu REST API:n kuvaukseen GeoServerin [ohjeessa](https://docs.geoserver.org/main/en/user/rest/). +::: + +## **Monitor-lisäosan käyttö** + +GeoServerin **Monitor**-lisäosa on valmiiksi asennettu koulutuspalvelimiin, ja se näkyy ylävalikossa oikealla. Tällä lisäosalla voidaan tarkastella GeoServerin liikennettä. Voit vapaasti tarkastella mitä Monitor-lisäosalla voi tehdä. Pohdi myös miten se voisi olla hyödyllinen teidän organisaatiossa. + +Avaa **Monitor → Activity**. + +Kokeile esikatsella eri tasoja ja katso sen jälkeen **Monitor → Activity → Daily**-osiosta järjestelmän aktiviteettigrafiikkaa, jonka pitäisi näyttää seuraavanlaiselta: + +![](img/harjoitus_10/image7.png) + +Avaa palvelukohtainen raportti **Monitor → Reports → OWS Request Summary**. + +![](img/harjoitus_10/image8.png) diff --git a/src/GS003/11_harjoitus_11.Rmd b/src/GS003/11_harjoitus_11.Rmd new file mode 100644 index 00000000..fb1712dd --- /dev/null +++ b/src/GS003/11_harjoitus_11.Rmd @@ -0,0 +1,376 @@ +# HARJOITUS 2.4: CSS-KUVAUSTEKNIIKKA + +**Harjoituksen sisältö** + +Harjoituksessa käsitellään eri aineistoille sovellettavia kuvaustekniikoita ja hyödynnetään CSS-tyylejä karttojen visualisoinnissa. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa hyödyntää peruskuvaustekniikoita GeoServerillä eri aineistojen visualisointiin käyttäen CSS-kieltä. + +**Arvioitu kesto** + +40 minuuttia. + +## **Valmistautuminen** + +Erilaisia aineistoja on ladattu GeoServer-palvelimelle. CSS-lisäosa on asennettu GeoServeriin. + +## **CSS-tyylit** + +Kun CSS-lisäosa on asennettu, voidaan kirjoittaa kuvaustekniikkatyylejä CSS-kielellä, joka SLD-kieleen verrattuna on käyttäjäystävällisempi tapa muokata kuvaustekniikkaa. + +Lisäosa toimii niin, että CSS-kielellä kirjoitettava koodi kääntyy SLD-kieleksi automaattisesti. + +## **Tyylin luominen** + +Luodaan nyt uusi tyyli **tiesto**-tasolle. + +Avaa ylävalikosta **Data → Styles** ja paina **Add a new style**. + +Määrittele uuden tyylin nimeksi **tiesto_css** ja jätä workspace-kohta tyhjäksi. Muista myös valita tyylin formaatiksi CSS. + +Valitse **Style Content** valikosta geometriatyypiksi **Line** ja paina **Generate**. + +**Style Editor** -laatikossa on nyt seuraavankaltainen teksti (väri vaihtelee): + +::: code-box +/\* \@title red line \*/\ +\* {\ +    stroke: #ff3300;\ +}\ +::: + +Paina **Save**. Avaa **Styles**-valikosta juuri luomasi tyyli ja valitse **Layer Preview** -välilehti. Paina "Preview on layer"-kohdan tasoa ja vaihda se tasoon helsinki:hki_tiesto, ellei se ole jo valittuna. + +Lähennä karttaa siten, että tiet alkavat näkyä kunnolla. + +Pystyt muokkaamaan CSS-koodia myös **Layer Preview** -välilehdellä. Huomaa merkki "**\***". Se tarkoittaa, että sääntö koskee kaikkia kohteita (samoin kuin \*-merkki esimerkiksi tiedostojen listauksessa komentoriviltä). + +![](img/harjoitus_11/image1.png) + +Vaihda nyt **stroke**-komennon arvoksi **blue**. + +::: code-box +\* { stroke: blue; +::: + +Paina **Apply** ja esikatsele karttaa. Nyt tiet näkyvät kartassa sinisinä. + +![](img/harjoitus_11/image2.png) + +## **Aineiston luokittelu** + +Luokitellaan nyt tiet kahteen luokkaan: **päätiet** ja **muut**. Kullekin luokalle voidaan määritellä oma tyyli. + +Päätiet luokitellaan korvaamalla merkki "**\***" seuraavalla koodilla (huomaa, että rajauksessa käytetään **fclass**-kentän tietoja): + +::: code-box +[fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\']\ +{ stroke: red;} +::: + +Paina **Apply**. Nyt vain päätiet ovat näkyvissä. + +Luo nyt tyyli muille teille. + +![](img/harjoitus_11/image3.png) + +Muokkaa CSS-tyyliä vielä seuraavasti: + +Kopioi pääteiden luokitus-koodi ja laita se koodilaatikon loppuun. + +Korvaa "**=**"-merkki "**eri kuin**"-merkillä "**\<\>**", niin tulee määrittelyksi tiet, jotka eivät kuulu edelliseen luokitukseen, eli toisin sanoen pienemmät tiet. + +Vaihda vielä "**or**"-komento "**and**"-komennoksi.  + +Kopioi sitten vielä edellisen CSS-tyylin koodinpätkä (aaltosulut ja niiden sisällä oleva koodi) ja liitä kopioitu koodi muiden teiden luokittelu-osion jälkeen. + +Vaihda muiden teiden tyyli niin, että viivan väri on musta (black). + +Pääteiden ja muiden teiden tyylit näyttävät nyt tältä: + +::: code-box +[fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\']\ +{ stroke: red;}\ +[fclass \<\> \'motorway\' and fclass \<\> \'primary\' and fclass \<\> \'secondary\' and fclass \<\> \'trunk\']\ +{ stroke: black;}\ +::: + +Esikatsele nyt tulos painamalla **Apply**. + +![](img/harjoitus_11/image4.png) + +## **Tyylin virittäminen** + +Tiet ovat vielä vaikea erottaa toisistaan. Tehdään nyt pari parannusta tyyliin. Määrittele pääteiden viivan paksuus 0.5:ksi komennolla "**stroke-width**". Lisää uusi rivi "**stroke: red;**"-kohdan jälkeen, johon kirjoitetaan "**stroke-width: 0.5;**". + +Määrittele samalla tavalla muille teille viivan paksuudeksi **0.1**: + +::: code-box +[fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\']\ +{ stroke: red;\ +stroke-width: 0.5;}\ +[fclass \<\> \'motorway\' and fclass \<\> \'primary\' and fclass \<\> \'secondary\' and fclass \<\> \'trunk\']\ +{ stroke: black;\ +stroke-width: 0.1;}\ +::: + +Paina sitten **Apply** niin esikatselu päivittyy: päätiet ja pienemmät tiet erottuvat paremmin. + +Kokeile seuraavaksi lisätä seuraava CSS-koodi, jonka avulla luodaan **pääteille** selkeämpi visualisointityyli. + +Poista vanha "**stroke**"-määrittely ensin. Tarkoituksena on visualisoida pääteitä keltaisen ja mustan viivan avulla siten, että keltainen viiva on kapeampi ja jää mustan viivan päälle.  Pyydä kouluttajalta apua tarvittaessa ja korvaa pääteiden tyyli seuraavalla koodilla CSS-tyylien muokkaustilassa: + +::: code-box +stroke: black, yellow;\ +stroke-width: 5px, 3px;\ +z-index: 0, 3; +::: + +Huomaa, että tällä tavalla määrittelet pääteille päällekkäiset tyylit, jotka muodostavat monimutkaisempia visualisointeja. + +Paina vielä **Save** ja visualisoi lopputulos: + +![](img/harjoitus_11/image5.png) + +**Z-index** -komennolla määritellään, missä järjestyksessä eri tyylit piirtyvät. Indeksin arvolla 0 viitataan mustaan viivaan, jonka paksuus määritellään viiteen pikseliin. Indeksin arvo kolme viittaa keltaiseen viivaan , jonka paksuus on kolme pikseliä. Huomaa, kuinka Z-indeksin pienemmät arvot piirtyvät ensin ja suuremmat arvot myöhemmin. + +::: hint-box +Psst! Huomaa, että esikatselun selite ei ymmärrä näitä monimutkaisia tyylejä oikein. Työkalu vaatii kehitystä, mutta nopeaa esikatselua varten kartta on riittävän hyvä. +::: + +Kokeile vaihtaa symbolien järjestystä, värejä ja paksuuksia. + +Koodisi näyttäisi tässä vaiheessa tältä: + +::: code-box + [fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\']\ +     { stroke: black, yellow;\ +     stroke-width: 5px, 3px;\ +     z-index: 0, 3;}\ +\ + [fclass \<\> \'motorway\' and fclass \<\> \'primary\' and fclass \<\> \'secondary\' and fclass \<\> \'trunk\']\ +     { stroke: black;\ +     stroke-width: 0.1;}\ +::: + +## **Luokkien nimeäminen** + +Lisätehtävänä voit muokata selitteen tekstejä (Legend) CSS-kielellä. Eri tasojen selitteen teksti määritellään "**/\* \@title**"-avaus ja "**\*/**"-sulkukomennoilla, ja ne sijoitetaan ennen hakasulkujen sisään asetettuja tasomäärityksiä seuraavalla tavalla. Tämän avulla saadaan nimet näkymään esikatselukartan selitteessä, ja samalla koodin lukeminen helpottuu. + +Laita pääteiden määrittelyn eteen **/\* \@title Paatiet \*/**: + +::: code-box + /\* \@title Paatiet \*/\ + [fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\']\ +     {stroke: black, yellow;\ +     stroke-width: 5px, 3px;\ +     z-index: 0, 3;}\ +::: + +Laita myös **/\* \@title Sivutiet \*/**: sivuteiden määrittelyn eteen samalla tavalla. + +Lisää vielä koodin loppuun seuraava koodi:  + +::: code-box +/\* \@title Tiesto \*/ \* { font-family: Helvetica; } +::: + +Edellä esitetty ja viimeinen taso/koodi määrittää "**\***"-merkin toimesta kokonaisvaltaisia CSS-tyylisääntöjä, joissa tässä on määritelty "**Tiesto**"-etuliite symboliikan teksteihin ja **Helvetica**-tyyli kartan tekstityksiin. + +Paina nyt **Apply**, jolloin kartta päivittyy. + +![](img/harjoitus_11/image6.png) + +Koodisi tässä vaiheessa pitäisi näyttää jotenkin tältä: + +::: code-box + /\* \@title Paatiet \*/\ + [fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\']\ +     { stroke: black, yellow;\ +     stroke-width: 5px, 3px;\ +     z-index: 0, 3;}\ +\ + /\* \@title Sivutiet \*/\ + [fclass \<\> \'motorway\' and fclass \<\> \'primary\' and fclass \<\> \'secondary\' and fclass \<\> \'trunk\']\ +     { stroke: black;\ +     stroke-width: 0.1;}\ +\ + /\* \@title Tiesto \*/\ +  \* { font-family: Helvetica;}\ +::: + +## **Mittakaavaperusteinen näkyvyys** + +Laitetaan vielä pätkä koodia, jonka avulla määrittelemme muille teille mittakaavaperusteinen rajaus, jotta ne eivät näkyisi kaikilla zoomaus-tasoilla: + +Laita pienempien teiden tyylin eteen: + +::: code-box +[\@scale \< 50000] +::: + +Paina **Apply**. + +Nyt muut tiet näkyvät vain, jos **zoom**-taso on tarkempi kuin **1:50 000**. Ja kun zoomataan kauemmaksi, niin vain päätiet näkyvät kartalla: + +![](img/harjoitus_11/image7.png) + +Koodisi tässä vaiheessa pitäisi näyttää jotenkin tältä: + +::: code-box + /\* \@title Paatiet \*/ + + [fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\'] + +     { stroke: black, yellow; + +     stroke-width: 5px, 3px; + +     z-index: 0, 3;} + +\ + + /\* \@title Sivutiet \*/ + + [\@scale \< 50000] + + [fclass \<\> \'motorway\' and fclass \<\> \'primary\' and fclass \<\> \'secondary\' and fclass \<\> \'trunk\'] + +     { stroke: black; + +     stroke-width: 0.1;} + +\ + + /\* \@title Tiesto \*/  + + \* { font-family: Helvetica;} +::: + +## **Tekstityksen lisääminen** + +Lisätään nyt tekstit teille.  + +Avaa **Layer Attributes** -välilehti ja tarkista, että muun muassa **name**-sarake löytyy listalta. Ota se käyttöön lisäämällä **label**-komento pääteiden koodiin: + +::: code-box +label: [name]; +::: + +Muuta vielä tekstien kirjaimia: + +::: code-box +font-weight: bold; font-fill: black; font-size: 12; +::: + +Esikatsele tuloksia painamalla **Apply**. + +Tulos ei ole vielä hyvä, tarvitaan muutama teksteihin tarkoitettu erikoiskomento: + +::: code-box +label-follow-line: true; label-max-angle-delta: 90; label-max-displacement: 400; label-repeat: 150; +::: + +**label-\*** kertoo, miten tekstit eli labelit piirretään. Kokeile muuttaa edellisten komentojen arvoja. + +![](img/harjoitus_11/image8.png) + +Lisää vielä puskuri teksteille: + +::: code-box +halo-color: white; halo-radius: 1; +::: + +Voit määritellä pienemmille teille myös nimiöt samalla tavalla kun äsken tehtiin pääteille. + +![](img/harjoitus_11/image9.png) + +Tässä on esimerkkikoodi, jossa kaikki aiemmin mainitut tyylit ovat käytössä: + +::: code-box + +/\* \@title Paatiet \*/ + +[fclass = \'motorway\' or fclass = \'primary\' or fclass = \'secondary\' or fclass = \'trunk\'] + +{ stroke: black, yellow; + +stroke-width: 5px, 3px; + +z-index: 0, 3; + +label: [name]; + +font-weight: bold; + +font-fill: black; + +font-size: 12; + +label-follow-line: true; + +label-max-angle-delta: 90; + +label-max-displacement: 400; + +label-repeat: 150; + +halo-color: white; + +halo-radius: 1;} + +\ + +/\* \@title Sivutiet \*/ + +[\@scale \< 50000] + +[fclass \<\> \'motorway\' and fclass \<\> \'primary\' and fclass \<\> \'secondary\' and fclass \<\> \'trunk\'] + +{ stroke: black; + +stroke-width: 0.1; + +label: [name]; + +font-fill: black; + +font-size: 9; + +label-follow-line: true; + +label-max-angle-delta: 90; + +label-max-displacement: 400; + +label-repeat: 150; + +halo-color: white; + +halo-radius: 1;} + +\ + +/\* \@title Tiesto \*/  + +\* { font-family: Helvetica;} + +::: + +Tämän tyylin esikatselu näyttää kartassa tältä: + +![](img/harjoitus_11/image10.png) + +Voit vielä kokeilla erilaisia komentoja virittämään karttasi tyyliä pidemmälle. GeoServer CSS-lisäosan nettisivuilla löytyy ohjeita ja esimerkkejä: https://docs.geoserver.org/latest/en/user/styling/css/index.html. + +## **Kuvaustekniikan soveltaminen** + +Avaa **Publishing**-välilehti ja aseta ruksi **Default**-sarakkeeseen tiestötason kohdalle. Paina vielä **Apply** asettaaksesi luomasi tyylin tiestötason oletustyyliksi. Tarkista **Data → Layers → tiesto → Publishing**-sivulta, että oletustyyli tälle tasolle on nyt **tiesto_css**. + +Voit vielä tehdä tyylipäivitykset **taustakartta**-tasolle. Avaa **Data → Layer Groups → taustakartta**, ja varmista, että **Layers**-taulussa **Default Style** on rastitettu kaikille tasoille. Paina lopuksi **Save**. + +Esikatsele **taustakartta**-taso. Lopputulos näyttää tältä: + +![](img/harjoitus_11/image11.png) diff --git a/src/GS003/12_harjoitus_12.Rmd b/src/GS003/12_harjoitus_12.Rmd new file mode 100644 index 00000000..244514b4 --- /dev/null +++ b/src/GS003/12_harjoitus_12.Rmd @@ -0,0 +1,227 @@ +# HARJOITUS 2.5: PÄÄSYNHALLINTA + +**Harjoituksen sisältö** + +Harjoituksessa tutustutaan erilaisiin turvallisuusasetuksiin ja luodaan käyttäjiä ja rooleja. Asetetaan käyttöoikeuksia palveluille, aineistoille ja käyttäjille. + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa asettaa palvelimen perusasetuksia, luoda käyttäjiä ja rooleja. Opiskelija osaa myös asettaa käyttöoikeuksia palveluille ja aineistoille. + +**Arvioitu kesto** + +40 minuuttia. + +## **GeoServerin yleiset turvallisuusasetukset** + +Avaa **Security → Settings**. Tässä näkymässä voit määritellä yleisesti käytössä olevia turvallisuusasetuksia. + +![](img/harjoitus_12/image1.png) + +Tässä näkymässä voidaan myös määrittää, mikä **Active role service** (roolipalvelu) on käytössä. GeoServer sallii valittavaksi vain yhden roolipalvelumuodon palvelimelle. Roolipalvelu  sisältää kaikki palvelimen käyttäjä-, ryhmä- ja roolisäännöt. Pidetään **default-**roolipalvelu käytössä. + +**Encryption**-kohdan alta löytyy salaukseen liittyviä asetuksia. **Encrypt web admin URL parameter** -valinta salaa käyttäjäliittymän URL-parametreja selaimella. + +Esimerkiksi, **Security**-valikon selaimen URL (missä nyt ollaan) olisi paikallisella GeoServer-asennuksella: + +`http://localhost:8080/geoserver/web/?wicket:bookmarkablePage=:org.geoserver.security.web.SecuritySettingsPage` + +... ja salauksen kanssa se on: + +`http://localhost:8080/geoserver/web/?x=xAiOmyK-CQmBXDhILZrWpny28bhFcImLrBRYk0INwrBDmbzQlmeLRdiaRoNSaBSx3o6elVC9JMPcy5gkRljLXMKGrvMx3Yui3*ANGkUW-hE` + +**Password encryption** määrittää, mikä salauksen tyyli on käytössä salasanojen varastossa: **Plain text**, **Weak PBE** ja **Strong PBE**. + +Valitse **Weak PBE** ja paina **Save**. + +## **Tunnistautuminen** + +**Authentication**-asetusten (tunnistautuminen) kautta määritellään, miten GeoServer kerää käyttäjätunnuksia ja verifioi niitä. + +Oletuksena GeoServerin asetukset sallivat palvelimen käytön (eli WMS-, WFS- ja WCS-palvelujen käytön) ilman mitään käyttäjätunnusta. Voit tarkistaa tämän seuraavasti. + +Esikatsele tasoa **ilmakuvat** samalla tavalla kuin aiemmin muita tasoja. Taso aukeaa selaimella ongelmitta, koska olet kirjautunut **admin**-tunnuksella.  Älä sulje esikatselusivua vielä. + +Mene nyt takaisin GeoServerin ylläpitosivulle ja paina **Logout**. + +Palaa esikatselusivulle ja lataa sivu uudelleen painamalla F5. Sivu latautuu edelleen normaalisti. + +Muutetaan asetuksia nyt niin, että palvelujen käyttö vaatii käyttäjältä tunnistautumisen, eivätkä kirjautumattomat käyttäjät pysty enää käyttämään palveluja. Kirjaudu uudestaan sisään. Avaa **Security → Authentication**, etsi **Filter Chains** ja paina **default**-linkkiä. + +Valitse nyt **Chain filters** -kohdasta, **Selected**-laatikosta **anonymous** ja siirrä se **Available**-laatikkoon käyttäen laatikkojen välistä löytyviä siirtonuolia. Anonymous-tunnistautumistyyli sallii palveluiden käytön tunnistautumattomille käyttäjille. + +![](img/harjoitus_12/image2.png) + +Nyt ainoastaan **basic**-tunnistautumisen tyyli on Selected-laatikossa eli käytössä. Tämä tarkoittaa sitä, että jatkossa tarvitaan aina tunnus ja salasana palvelimen käyttöön. + +![](img/harjoitus_12/image3.png) + +Paina nyt **Close** ja pääset takaisin **Authentication** valikkoon. Paina sitten **Save**, niin muutokset tulevat voimaan. + +Jos kirjaudut nyt ulos GeoServeristä ja päivität aiemmin avattua esikatselusivua uudelleen, tunnuksella ja salasanalla tunnistautuminen on pakollista. + +::: hint-box +Psst! Palvelimen pääsynhallinnan asetuksia voi helpoiten testata käyttämällä eri web-selainta (Internet Explorer / FireFox / Chrome) tai selaimen yksityisyyden salaavaa toimintoa (Incognito-mode) normaalin selaimen rinnalla. Toisella selaimella ollaan kirjautuneet ylläpitäjän tunnuksilla (kuten tähän asti) ja toisella kokeillaan erilaisia kirjautumistiloja. Voit kysyä opastusta kouluttajalta tarvittaessa. Huomaa, että mahdollisesti sinun täytyy kirjautua ulos ja uudelleen sisään asetusten uudelleen lataamista varten. +::: + +## **Uuden roolin ja käyttäjän luominen** + +GeoServerin pääsynhallinnassa keskeisessä asemassa ovat käyttäjä-, ryhmä- ja rooliasetukset. + +Luodaan nyt uusi käyttäjä, jolla on vain oikeuksia käsitellä **helsinki**-workspace:n aineistoa. + +Avaa päävalikosta **Security → Users, Groups, Roles**. + +**Services**-välilehdestä löytyvät käyttäjien ja roolien pääsynhallinnan asetukset. Niihin tallennetaan tietoja siitä, ketkä käyttäjät kuuluvat mihinkin ryhmään tai mitä rooleja on ylipäätänsä määritelty pääsynhallinnassa. + +Listalla on GeoServerin luomia **default**-asetuksia. Käytetään niitä nyt hyväksi. + +Luodaan ensin rooli, johon uusi käyttäjä tulee kuulumaan. + +::: hint-box +Psst! Pääsynhallinnan hallinnoimisen helpottamiseksi, oikeudet määritellään roolikohtaisesti. +::: + +Avaa **Roles**-välilehti ja paina **Add new role**, anna uudelle roolille nimeksi **hki_rooli**. + +![](img/harjoitus_12/image4.png) + +Paina sitten **Save**. + +Luodaan nyt uusi käyttäjä. Avaa **Users/Groups** -välilehti. + +Huomaa, että täällä on määritelty **admin**-tunnus, jota olet käyttänyt koulutuksen aikana. + +![](img/harjoitus_12/image5.png) + +Luo uusi käyttäjä painamalla **Add new user**. + +Laita käyttäjän nimeksi **hki_kayttaja** ja salasanaksi **hki-kayttaja**. + +::: hint-box +Psst! Huomaa, että erikoismerkit ja ääkköset ovat sallittuja GeoServerissä, mutta tarkoituksenmukaisuuden ja eri selainten välisten erojen vuoksi tässä koulutuksessa vältetään niiden käyttöä. +::: + +Uudelle käyttäjälle annetaan oikeuksia määrittämällä mitä rooleja käyttäjällä on. Roolit on määritelty **Roles taken from active role service: default** -kohdassa. Siinä on valittavissa muun muassa **hki_rooli**, lisää se **Selected Roles** -laatikkoon käyttäen laatikkojen välistä löytyviä siirtonuolia. + +Paina sitten **Save**. Uusi käyttäjä **hki_kayttaja** on luotu, ja sillä on oikeudet **hki_rooli**nmukaisesti. Koska **hki_rooli**lle ei vielä ole määritelty oikeuksia, niin uudella käyttäjälläkään ei ole vielä määritelty mitään oikeuksia. + +![](img/harjoitus_12/image6.png) + +Kokeile uuden käyttäjän oikeudet kirjautumalla sisään uudella käyttäjätunnuksella. Voit joko kirjautua uudestaan tai käyttämällä toista selainta, kuten aikaisemmin on mainittu. + +## **Aineiston käyttöoikeuksien rajoittaminen** + +Avaa **Security → Data.** Tässä näkymässä näkyvät GeoServer-palvelimen käyttäjille (rooleille) määritetyt säännöt. Oletusasennuksessa vain kaksi sääntöä on määritelty eli luku (r) ja kirjoitusoikeudet (w): + +![](img/harjoitus_12/image7.png) + +**Rule path** -sarakkeen alla olevat säännöt ovat muodoltaan **\*.\*.r**, joka antaa lukemisoikeudet kaikille tasoille kaikissa workspaceissa. + +Huomaa vielä, että **Roles**-sarakkeen alla on määritelty, mille rooleille oikeudet on myönnetty. Tässä on määritelty **\***-merkillä, joka tarkoittaa "kaikki roolit". + +Seuraava kuva esittää GeoServerin aineistosääntöjen määrittelyä: + +![](img/harjoitus_12/image8.png) + +Sääntö koostu kolmesta osasta, pisteellä eroteltuina. Ensimmäinen osa määrittelee workspacen, jolla sääntö on voimassa. Toinen osa määrittelee tason (layer), jolla sääntö on voimassa. Kolmas ja viimeinen osa määrittelee, mikä toiminto sallitaan. Toimintoja on kolme: lukeminen (r = read), kirjoittaminen (w = write) ja ylläpitäminen (a = admin). + +::: hint-box +Psst! Kaikissa osissa voidaan käyttää myös \*-merkkiä, jolla voidaan viitata kaikkiin workspaceihin, tasoihin tai oikeuksiin. +::: + +Paina listalta **\*.\*.r**. + +Tässä säännössä määritellään, että kaikkiin workspaceihin ja kaikkiin tasoihin on lukemisoikeus. + +![](img/harjoitus_12/image9.png) + +**Roles**-osion alla on määritelty se, että tämä sääntö kuuluu kaikille rooleille (**Grant access to any role**). Huomaa, että **Grant access to any role** on rastittu, joten kaikki roolit voivat lukea kaikki tasoja kaikista workspaceista. + +Rajoitetaan nyt sääntöä vain ylläpitäjien rooleille. + +![](img/harjoitus_12/image10.png) + +Rastita pois **Grant access to any role** ja lisää roolit **Admin** ja **GROUP_ADMIN** laatikkoon **Selected Roles**. + +Paina lopuksi **Save**. + +Tee lopuksi tarvittaessa samat muutokset **\*.\*.w** -säännölle. + +![](img/harjoitus_12/image11.png) + +Näiden muutosten jälkeen, vain **Admin**-tunnuksella on lukemis- ja esikatseluoikeudet. + +Kokeile nyt esikatsella jotain aineistoja **hki_kayttaja**-tunnuksella, yhtään aineistoa ei ole tarjolla. + +![](img/harjoitus_12/image12.png) + +## **Aineiston käyttäjäkohtaiset käyttöoikeudet** + +Seuraavaksi määritellään **hki_kayttaja**-tunnukselle luku- ja kirjoitusoikeudet **helsinki**-workspaceen. Käytännössä pitää määritellä oikeudet roolille johon käyttäjä kuuluu, eli **hki_rooli**-roolille. + +Tähän asti **hki_rooli**lle ei ole annettu vielä mitään oikeuksia, täten **hki_kayttaja**llä ei ole enää oikeuksia mihinkään aineistoon (ellei joku sääntö on määritelty kaikille rooleille käyttäen **\***-merkkiä). + +Lisätään nyt **hki_kayttaja**lle **Read**- ja **Write**-käyttöoikeudet, mutta vain **helsinki-**workspaceen. Tämä tapahtuu määrittämällä lisää aineiston sääntöjä. + +::: hint-box +Psst! Varmista, että olet kirjautunut ylläpitäjänä (tunnus: admin ja salasana: gispo). +::: + +Avaa taas **Security → Data** ja lisää nyt kaksi uutta sääntöä käyttäen **Add new rule** -valintaa. + +Laita **Workspace**-kohtaan **helsinki** ja jätä **Layer**-arvoksi "**\***", joka viittaa kaikkiin tasoihin. + +Aseta **Access mode** -valikkoon arvoksi **Read**, joka antaa lukemisoikeudet edellä määrätyille aineistoihin. + +Siirrä lopulta **hki_rooli** laatikosta **Available Roles**, laatikkoon **Selected Roles**. + +![](img/harjoitus_12/image13.png) + +Paina lopuksi **Save**.  + +Nyt **hki_kayttaja**llä on read-oikeudet kaikkiin helsinki workspacen tasoihin. + +Lisää samalla tavalla vielä **Write**. Kirjoitusoikeudet sallivat käyttäjälle aineistojen muokkausoikeudet aina kun se on sallittu GeoServerissa, esimerkiksi WFS-T-palvelun kautta. + +Kokeile uudelleen esikatsella aineistoja **hki_kayttaja**-tunnuksella. Esikatselu on mahdollista nyt kaikkien **helsinki**-workspacen tasojen kanssa, mutta muut tasot eivät näy. + +Huomaa, että käyttäjällä ei ole kuitenkaan oikeuksia lisätä GeoServeriin uusia aineistoja tai tasoja. Voit antaa **hki_kayttaja**-tunnukselle **Admin**-oikeudet määrittämällä vielä ylläpitäjän oikeudet. + +Loppujen lopuksi on luotu kolme uutta sääntöä, jotka ovat **hki_rooli**lla voimassa. + +![](img/harjoitus_12/image14.png) + +Kirjaudu uudelleen palvelimelle käyttäen **hki_kayttaja**-tunnusta ja tarkista, että **helsinki**-workspacen tasot ovat käytettävissä ja aineiston ylläpitäminenkin onnistuu. Huomaa, että muut workspacet eivät näy lainkaan.\ + +## **Karttapalvelukohtaiset oikeudet** + +Kirjaudu **admin**-tunnuksella uudelleen GeoServer palvelimelle. + +Samalla tavalla kuin äsken määriteltiin käyttäjäkohtaisesti oikeudet yhdelle workspacelle, voidaan asettaa oikeudet palvelukohtaisesti eri käyttäjille. + +Avaa **Security → Services**. + +Seuraava kuva esittää GeoServerin palvelusääntöjen määrittelyä: + +![](img/harjoitus_12/image16.png) + +Luo uusi sääntö **\*.\*** ja rajoita sen käyttöä vain ylläpitäjille. + +![](img/harjoitus_12/image17.png) + +Paina **Save**. Nyt GeoServerin palvelut ovat käytettävissä ainoastaan ylläpito-rooleille. + +Luo sen jälkeen sääntö, joka on käytössä vain **hki_rooli**lle ja sillä on oikeudet käyttää kaikkia **WMS-karttapalvelujen** toimintoja. + +::: hint-box +Psst! Huomaa, että säännöt eivät välttämättä tule voimaan kuin vasta käyttäjän seuraavalla istunnolla. +::: + +Lopussa **Service access rules list** -näkymä näyttää tältä: + +![](img/harjoitus_12/image18.png) + +Kirjaudu taas **hki_kayttaja**nä ja kokeile esikatsella jotain aineistoa sekä WMS- että WFS-karttapalveluna. WFS-esikatselu ei enää onnistu, koska kyseisellä käyttäjällä ei ole oikeuksia WFS-toimintoihin, vain WMS-toimintoihin.  + +Poista lopuksi edellisessä kuvassa näkyvät säännöt (\*.\* ja wms.\*), jotta WMS-palvelu olisi taas käytettävissä kaikilla käyttäjillä. diff --git a/src/GS003/13_harjoitus_13.Rmd b/src/GS003/13_harjoitus_13.Rmd new file mode 100644 index 00000000..5eec0b73 --- /dev/null +++ b/src/GS003/13_harjoitus_13.Rmd @@ -0,0 +1,69 @@ +# HARJOITUS 2.6: SUODATTIMET + +**Harjoituksen sisältö** + +Harjoituksessa tutustutaan CQL- (Common Query Language) ja ECQL-suodattimeen (Extended CQL). + +**Harjoituksen tavoite** + +Harjoituksen jälkeen opiskelija osaa muodostaa ominaisuus- ja geometriatietoihin perustuvia suodattimia. + +**Arvioitu kesto** + +25 minuuttia. + +## **Valmistautuminen** + +GeoServeriin on asennettu palvelinkone ja siihen voi muodostaa yhteyden.  + +## **Ominaisuustietosuodattimet** + +Mene **Browse Layers**-valikkoon ja avaa **topp:states**-aineiston esikatselu painamalla **OpenLayers**-linkkiä: + +![](img/harjoitus_13/image1.png) + +Saat karttaikkunan vasemmalla puolella olevat valikot näkyviin, kun klikkaat kartan vasemmassa ylänurkassa olevaa nappulaa. + +Voit suodattaa kartalla näkyviä kohteita **CQL (Common Query Language)**-kyselylausekkeilla. CQL tukee vertailuoperaattoreita kuten = (yhtä suuri kuin), \< (pienempi kuin), \<= (pienempi tai yhtä suuri kuin) ja \<\> (eri kuin).  + +Yksinkertaisimmillaan nämä kyselylauseet voivat siis olla esim. muotoa fieldname = 1 (näytä vain kohteet, joilla kenttä = 1) tai value \> 0 ( näytä vain kohteet, joilla arvo suurempi kuin 0). + +Kirjoita hakulausekkeet ylös omiin muistiinpanoihin. + +::: hint-box +Missä osavaltioissa väkiluku on yli 15 miljoonaa asukasta? +Psst! Klikkaa ensin osavaltiota ja tarkista väkiluvun sisältävän ominaisuustietokentän nimi. +::: + +::: hint-box +Minkä osavaltioiden väkiluku on yli miljoona, mutta vähemmän kuin kolme miljoonaa? Montako osavaltiota piirtyy kartalle? +Psst! Voit käyttää operaattoria “BETWEEN - - AND - -”. +::: + +::: hint-box +Minkä osavaltioiden nimet alkavat C-kirjaimella? +Psst! Käytä operaattoria “LIKE” ja villikortti-symbolia “%”. Laita osavaltion nimi lainausmerkkien sisään (‘ ‘). +::: + +::: hint-box +Missä osavaltioissa on enemmän miespuolisia asukkaita? +::: + +::: hint-box +Mitkä osavaltiot leikkaavat 100. asteen läntistä pituuspiiriä? +Psst! Lauseke on muotoa “BBOX(geometriasarake, xmin, ymin, xmax, ymax)”. Osavaltioiden geometria on kentässä “the_geom”. +::: + +GeoServerin [dokumentaatiosta](https://docs.geoserver.org/main/en/user/tutorials/cql/cql_tutorial/#simple-comparisons) löytyy tarkempia kuvauksia. + +::: hint-box +Suodata **topp:states**-tason WMS-palvelua siten, että ainoastaan ne osavaltiot, joiden työttömyysprosentti on alle 5 % näkyvät palvelussa. +::: + +::: hint-box +Muodosta topp:states-tasolle kuvaustekniikka, jossa piirretään osavaltioiden keskipisteeseen punainen symboli. +::: + +::: hint-box +Yhdistä edelliseen kuvaustekniikkaan osavaltioiden rajat harmaalla värillä. +::: diff --git a/src/GS003/404.html b/src/GS003/404.html new file mode 100644 index 00000000..2cc9f4b4 --- /dev/null +++ b/src/GS003/404.html @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +Page not found | yaml_title_replace + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ +
+
+
+
+
+

Page not found

+

The page you requested cannot be found (perhaps it was moved or renamed).

+

You may want to try searching to find the page's new location, or use +the table of contents to find the page you are looking for.

+
+

+

+
+
+ +
+ + +
+ +
+ + + + + + + diff --git a/src/GS003/css/bootstrap.min.css b/src/GS003/css/bootstrap.min.css new file mode 100644 index 00000000..8ceefe5e --- /dev/null +++ b/src/GS003/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;/* flex:0 0 100%; */max-width:100%;}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal .list-group-item.active{margin-top:0}.list-group-horizontal .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm .list-group-item.active{margin-top:0}.list-group-horizontal-sm .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md .list-group-item.active{margin-top:0}.list-group-horizontal-md .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg .list-group-item.active{margin-top:0}.list-group-horizontal-lg .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl .list-group-item.active{margin-top:0}.list-group-horizontal-xl .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush .list-group-item{border-right-width:0;border-left-width:0;border-radius:0}.list-group-flush .list-group-item:first-child{border-top-width:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/src/GS003/css/hamburgers.css b/src/GS003/css/hamburgers.css new file mode 100644 index 00000000..8e49de35 --- /dev/null +++ b/src/GS003/css/hamburgers.css @@ -0,0 +1,93 @@ +/*! + * Hamburgers + * @description Tasty CSS-animated hamburgers + * @author Jonathan Suh @jonsuh + * @site https://jonsuh.com/hamburgers + * @link https://github.com/jonsuh/hamburgers + */ +.hamburger { + padding: 15px 15px; + display: inline-block; + cursor: pointer; + transition-property: opacity, filter; + transition-duration: 0.15s; + transition-timing-function: linear; + font: inherit; + color: inherit; + text-transform: none; + background-color: transparent; + border: 0; + margin: 0; + overflow: visible; } + .hamburger:hover { + opacity: 0.7; } + .hamburger.is-active:hover { + opacity: 0.7; } + .hamburger.is-active .hamburger-inner, + .hamburger.is-active .hamburger-inner::before, + .hamburger.is-active .hamburger-inner::after { + background-color: #fbfbfb; } + +.hamburger-box { + width: 40px; + height: 24px; + display: inline-block; + position: relative; } + +.hamburger-inner { + display: block; + top: 50%; + margin-top: -2px; } + .hamburger-inner, .hamburger-inner::before, .hamburger-inner::after { + width: 40px; + height: 4px; + background-color: #000; + border-radius: 4px; + position: absolute; + transition-property: transform; + transition-duration: 0.15s; + transition-timing-function: ease; } + .hamburger-inner::before, .hamburger-inner::after { + content: ""; + display: block; } + .hamburger-inner::before { + top: -10px; } + .hamburger-inner::after { + bottom: -10px; } + +/* + * Arrow + */ +.hamburger--arrow.is-active .hamburger-inner::before { + transform: translate3d(-8px, 0, 0) rotate(-45deg) scale(0.7, 1); } + +.hamburger--arrow.is-active .hamburger-inner::after { + transform: translate3d(-8px, 0, 0) rotate(45deg) scale(0.7, 1); } + +/* + * Collapse + */ +.hamburger--collapse .hamburger-inner { + top: auto; + bottom: 0; + transition-duration: 0.13s; + transition-delay: 0.13s; + transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } + .hamburger--collapse .hamburger-inner::after { + top: -20px; + transition: top 0.2s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), opacity 0.1s linear; } + .hamburger--collapse .hamburger-inner::before { + transition: top 0.12s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19); } + +.hamburger--collapse.is-active .hamburger-inner { + transform: translate3d(0, -10px, 0) rotate(-45deg); + transition-delay: 0.22s; + transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } + .hamburger--collapse.is-active .hamburger-inner::after { + top: 0; + opacity: 0; + transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333), opacity 0.1s 0.22s linear; } + .hamburger--collapse.is-active .hamburger-inner::before { + top: 0; + transform: rotate(-90deg); + transition: top 0.1s 0.16s cubic-bezier(0.33333, 0, 0.66667, 0.33333), transform 0.13s 0.25s cubic-bezier(0.215, 0.61, 0.355, 1); } diff --git a/src/GS003/custom.css b/src/GS003/custom.css new file mode 100644 index 00000000..5062cb1d --- /dev/null +++ b/src/GS003/custom.css @@ -0,0 +1,508 @@ +/* Import fonts */ +@import url("https://fonts.googleapis.com/css2?family=Karla:wght@300;400;700&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;700;800&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300;400;700&display=swap"); +@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"); + + +/* to-do */ +.todo { + background-color: red; + color:#eeeeee; +} + + +.in-view { + background-color: #ab8b9b; + + padding-left: 3%; + padding-top: 2%; + padding-bottom: 2%; + padding-right: 10%; + + border-radius: 0.5em; +} + +/* Generics */ +body { overflow-x: hidden; } + +p, span, li { + color: #434662; + font-family: 'Karla', sans-serif; + font-size: 1em !important; + text-align: left; + line-height: 1.6em !important; +} + +a { + color: #936287 !important; + text-decoration: underline !important; + background-color: transparent; +} + +a:hover { + color: #caabbe !important; + text-decoration: underline; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Inter', sans-serif !important; + font-weight: bold !important; + color: #434662; + line-height: 140%; + margin-bottom: 0.6em !important; + margin-top: 1em !important; +} + +h1 { font-size: 240% !important; } + +h2 { font-size: 200% !important; } + +h3 { font-size: 160% !important; } + +h4 { font-size: 120% !important; } + +h5 { font-size: 100% !important; } + +/* Sidebar and toc */ + +.sidebar { + background-color: #7a3884; + color: #fbfbfb; + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 100; + width: 20%; + overflow-y: visible; +} + +.sidebar-wrapper { + padding: 5% 10%; + width: 100%; + height: 100%; + overflow: scroll; +} + +.sidebar-wrapper::-webkit-scrollbar { + width: 0 !important; + background: transparent !important; +} + +.sidebar-title-wrapper { + margin: 10% 5% 20% 5%; +} + +.sidebar-title { + text-align: left !important; +} + +.sidebar-title h1 { + color: #fbfbfb !important; +} + +.sidebar-title:after { + content: ""; + background: #fbfbfb; + height: 0.01em; + width: auto; + display: block; + margin-top: 2em; + margin-bottom: 2em; +} + +.sidebar-logo { + display: block; + margin-bottom: 1.8em; +} + +.abstract-sidebar p { + color: #fbfbfb; + font-size: 90%; +} + +.sidebar-nav { + margin: 0; +} + +.sidebar .navbar-toggle { + display: none; +} + +#TOC ul { + position: relative; + padding-inline-start: 6%; +} + +#TOC li { + list-style: none; + padding-bottom: 0.6em; +} + +#TOC > ul > li > a { + height: auto; + font-family: 'Inter', sans-serif; + font-size: 120%; + font-weight: bold; + padding-bottom: 0.6em; + text-decoration: none !important; +} + +#TOC li > a { + color: #fbfbfb !important; + width: 100%; + max-width: 300px; + display: flex; + align-items: flex-start; + flex-direction: row; + justify-content: flex-start; + font-family: 'Inter', sans-serif; + text-decoration: none !important; +} + +#TOC li > a:hover { text-decoration: underline !important; } + +.toc-section-number { + color: inherit; + padding-right: 0.4em; + margin-bottom: 0.2em !important; + display: none; +} + +/* Main content */ + +.main-content { + background-color: #fff; + display: flex; + position: relative; + min-height: 100vh; + height: auto; + width: auto; + justify-content: space-between; + flex-direction: column; + align-items: stretch; +} + +.header-section-number { + display: none; +} + +.main-content-wrapper { padding-bottom: 15%; } + +.level1 { /* wraps around main content */ + flex: 1 0 auto; + justify-content: space-between; + margin-left: 20%; + padding: 2em 3em; + max-width: 60%; + height: auto; +} +.level1 img { + max-width: 100%; + padding: 1em 0; +} + +.level1 .figure { padding: 1em 0; } + +.level1 p, .level1 span { margin-bottom: 1rem !important; } + +/* Title */ + +#title-container { + text-align: left; + margin-left: 20%; + padding: 2em 3em; +} + +#title-container:after { + content: ""; + background: #434662; + display: block; + position: relative; + height: 0.01em; + width: auto; + margin-top: 2em; +} + +/* Tables */ + +table .header { + background-color: #7a3884; + color: #fbfbfb !important; +} + +table .odd { + background-color: #FCFCFC; +} + +table .even { + background-color: #F5F5F5; +} + +table th, +table td { + border-right: 1px solid #ffffff; + padding: 0.8em; + font-family: 'Karla', sans-serif; +} + +/* Buttons */ + +.btn { + font-weight: bold !important; + font-family: 'Inter', sans-serif; + color: #fbfbfb !important; + background-color: #7a3884 !important; + border: none !important; + margin: 0.8em 0.8em !important; + padding: 0.4em 0.8em !important; + border-radius: 3 !important; + display:block +} + +.btn:hover { + color: #fbfbfb !important; + text-decoration: none; + background-color: #ab8b9b !important; +} + +.copy-button { + visibility: hidden; + position: absolute; + top: 0px; + right: 0px; + width: 2.5rem; + height: 2.5rem; + padding: 5px; + background-color: transparent; + border: 1px solid #434662; + border-radius: 3px; + cursor: pointer; + } + +.copy-button{ + color: #8588a2; +} + +.copy-button:hover{ + color: #434662; +} + +.copy-button::before { + font-family: "Font Awesome 5 Free"; + font-weight: 900; + content: "\f0c5"; + } + +.copy-button.show-checkmark::before { + content: "\f00c"; + color:#0f9400; +} + +.code-box:hover .copy-button, +.commandline-box:hover .copy-button { + visibility: visible; + } + + +/* Footer */ + +.footer { + display: flex; + position: absolute; + bottom: 0; + width: 100%; + height: auto; + padding-top: 4em; + padding-bottom: 2em; + flex-shrink: 0; + margin: 0; + flex-direction: column; + align-items: flex-end; +} + +.footer:before { + content: ""; + background-image: url(img/Gispo_tausta.png); + background-repeat: no-repeat; + background-size: cover; + display: block; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + opacity: 0.4; + z-index: 1; + clip-path: polygon(0 10%, 20% 15%, 40% 35%, 54% 20%, 66% 40%, 78% 10%, 85% 15%, 90% 0%, 100% 0, 100% 100%, 0 100%); +} + +.footer-container { + display: flex; + align-items: flex-start; + justify-content: space-around; + z-index: 2; +} + +.footer-info { + padding: 0 1.6em; + text-align: right; +} + +.footer-version { position: relative; } + +.license { padding-top: 10%; } + +.footer-contact { + margin: 0 20px; +} +.footer-contact-info { + display: block; + text-align: right; +} + +.footer-copyright-year { font-size: 0.5rem; } + +/* Content boxes */ + +.hint-box { + background: #434662; + padding: 10px; + border: none; + margin-bottom: 1rem; + border-radius: 3px; +} + +.hint-box p { + color: #fbfbfb; + margin: 0 !important; +} + +.note-box { + background: #ffffff; + padding: 10px; + border: none; + margin-bottom: 1rem; + border-radius: 3px; +} + +.note-box p { + color: #434662; + margin: 0 !important; +} + +.code-box { + position: relative; + background: #f5d2cc; + padding-top: 0.1rem; + padding-bottom: 0.1rem; + padding-left: 1rem; + padding-right: 1rem; + border: none; + display: flex; + align-items: center; + margin-bottom: 1rem; + min-height: 2.5rem; + border-radius: 3px; +} + +code span { + font-family: 'Source Code Pro', monospace; +} + +.code-box p { + color: #000000; + font-family: 'Source Code Pro', sans-serif !important; + margin: 0 !important; +} + +.file-content-box { + background: #caabbe; + padding: 10px; + border: none; + margin-bottom: 1rem; + border-radius: 3px; +} + +.file-content-box p { + color: #000000; + margin: 0 !important; +} + +.commandline-box { + position: relative; + background: #cccccc; + padding-top: 0.1rem; + padding-bottom: 0.1rem; + padding-left: 1rem; + padding-right: 1rem; + border: none; + display: flex; + align-items: center; + margin-bottom: 1rem; + min-height: 2.5rem; + border-radius: 3px; +} + +.commandline-box p { + font-family: 'Source Code Pro', monospace; + color: #000000; + margin: 0 !important; +} + +.hidden-box { + display: none; + padding: 1rem; + background: #f0f0f079; +} + +/* Media queries */ +@media (max-width: 1540px) { + .sidebar { width: 320px; } + .main-content-wrapper { padding-bottom: 200px; } + #title-container { margin-left: 320px; } + .level1 { + margin-left: 320px; + width: calc(100% - 320px); + } +} + +@media (max-width: 1199px) and (min-width: 992px) { .level1 { padding: 80px 40px 25px; } } + +/* Hide sidebar */ +@media (max-width: 991px) { + .sidebar { + width: 320px; + left: -320px; + transition: all 0.3s ease; + } + + .sidebar.show { left: 0; } + .sidebar-wrapper { padding: 15px; } + + .sidebar .navbar-toggle { + display: inline-block; + position: fixed; + left: 20px; + padding: 10px 15px; + transition: all 0.3s ease; + border-radius: 0; + outline: none; + } + + + #title-container { margin-left: 0; } + .level1 { + margin-left: 0; + padding: 50px 40px; + width: 100%; + max-width: none; + } + .footer { margin-left: 0; } +} + +@media (max-width: 767px) { + .level1 { padding: 60px 10px; } +} + diff --git a/src/GS003/custom.html b/src/GS003/custom.html new file mode 100644 index 00000000..cf990842 --- /dev/null +++ b/src/GS003/custom.html @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + +$if(url)$$endif$ +$if(cover-image)$$endif$ +$if(description)$$endif$ +$if(github-repo)$$endif$ + +$for(author-meta)$ + +$endfor$ + +$if(date-meta)$ + +$endif$ + +$if(math)$ + +$math$ +$endif$ + + + +$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ + +$for(header-includes)$ +$header-includes$ +$endfor$ + +$if(highlightjs)$ + + + + +$endif$ + +$if(highlighting-css)$ + + +$endif$ + +$for(css)$ + +$endfor$ + + + + + + + + +$for(include-before)$ +$include-before$ +$endfor$ + +
+ + + +
+ + + +
+ +$if(title)$ +

$title$

+$if(subtitle)$ +

$subtitle$

+$endif$ + +$for(author)$ +$if(author.name)$ +

$author.name$

+ +$if(author.affiliation)$ +
$author.affiliation$
+$endif$ + +$if(author.email)$ +$author.email$
+$endif$ +$else$ +

$author$

+$endif$ +$endfor$ + +$if(date)$ +

Version: $date$

+$endif$ + +$if(abstract)$ +

$abstract$

+$endif$ + +$endif$ +
+ + + + + + + +$body$ + + +
+ + +
+ +
+ +$for(include-after)$ +$include-after$ +$endfor$ + + + + + + diff --git a/src/GS003/img/Gispo_tausta.png b/src/GS003/img/Gispo_tausta.png new file mode 100644 index 00000000..9a59898d Binary files /dev/null and b/src/GS003/img/Gispo_tausta.png differ diff --git a/src/GS003/img/by-nd.svg b/src/GS003/img/by-nd.svg new file mode 100644 index 00000000..6efd00d8 --- /dev/null +++ b/src/GS003/img/by-nd.svg @@ -0,0 +1,203 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/GS003/img/gispo_white.png b/src/GS003/img/gispo_white.png new file mode 100644 index 00000000..6bd5633c Binary files /dev/null and b/src/GS003/img/gispo_white.png differ diff --git a/src/GS003/img/gispo_white_sm.png b/src/GS003/img/gispo_white_sm.png new file mode 100644 index 00000000..678ab928 Binary files /dev/null and b/src/GS003/img/gispo_white_sm.png differ diff --git a/src/GS003/img/gispologosvg.svg b/src/GS003/img/gispologosvg.svg new file mode 100644 index 00000000..1a0bfada --- /dev/null +++ b/src/GS003/img/gispologosvg.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/GS003/index.Rmd b/src/GS003/index.Rmd new file mode 100644 index 00000000..9e92551f --- /dev/null +++ b/src/GS003/index.Rmd @@ -0,0 +1,94 @@ +--- +title: "Johdanto GeoServerin käyttöön" +author: "Gispo Oy" +date: "`r Sys.Date()`" +site: "bookdown::bookdown_site" +abstract: "Johdantokurssi GeoServerin käyttöön" +output: + bookdown::html_book: + toc: true + theme: null + template: custom.html + css: custom.css + split_by: chapter+number #rmd +documentclass: book +--- + + + +# JOHDANTO GEOSERVERIN KÄYTTÖÖN + +## Tervetuloa GeoServer-kurssille! +Tämän kurssin tarkoituksena on opastaa sinut GeoServerin käyttöön. Paikkatietojen tai muiden paikkatieto-ohjelmistojen käyttökokemus ei ole välttämätöntä koulutukseen osallistumisel­le. Toki niistä on paljon hyötyä. Kysy rohkeasti opettajalta lisätietoja, jos käytetty terminologia tai joku asia jää epäselväksi. + +## GeoServerin taustaa +GeoServer on avoimen lähdekoodin palvelinohjelmisto, jossa voi jakaa, käsitellä ja hallinnoida geospatiaalista dataa. GeoServer on ollut toiminnassa vuodesta 2001 alkaen “The Open Planning Project”-hankkeen kautta. Vuonna 2014 GeoServer siirtyi OSGeo-projektin alle. GeoServeriä päivitetään puolen vuoden välein ja ohjelmiston käyttäjäkunta on varsin vilkas ja aktiivinen. + + +## Lukuohje + +**Web-selaimessa** suoritettavat komennot on merkitty seuraavasti: + +::: note-box + +::: + +**Komentorivillä** suoritettavat komennot on merkitty seuraavasti: + +::: commandline-box +sudo lsb_release -a +::: + +**Tiedostojen sisältö** on esitetty seuraavasti: + +::: file-content-box +#IPv4 local connections: #host all all 127.0.0.1/32 md5 +::: + +**HTML- ja CSS-koodit, SQL-komennot sekä Python-scriptit** on esitetty seuraavasti: + +::: code-box +SELECT version(); +::: + +**Kysymykset, lisätiedot ja vihjeet** on esitetty seuraavasti: + +::: hint-box +Mistä löydät tietoa GeoServeristä? + +GeoServerillä on omat kotisivut. + +Psst! Tietoa GeoServeristä löydät internetistä. +::: + +## Mistä lisätietoja? + +Ohessa muutamia web-sivustoja, joista saat lisätietoja: + +::: note-box + +::: + +::: note-box + +::: + +::: note-box + +::: + +::: note-box + +::: + +::: note-box + +::: + +## Virheet + +Jos havaitset materiaalissa virheitä, pyydämme ilmoittamaan niistä kouluttajalle tai sähköpostilla [info\@gispo.fi-osoitteeseen](mailto:info@gispo.fi-osoitteeseen){.email}. + +## Lisenssi ja oikeudet + +Nämä materiaalit on "Johdanto GeoServerin käyttöön" -kurssille kehittänyt Gispo Oy (nyk. Gispo Suomi Oy). Materiaalit on lisensoitu seuraavalla lisenssillä: [CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0/) diff --git a/src/GS003/index.html b/src/GS003/index.html new file mode 100644 index 00000000..2d254291 --- /dev/null +++ b/src/GS003/index.html @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +yaml_title_replace + + + + + + + + + + + + + + + + +
+ + + +
+ +
+
+ +
+
+
+
+ +
+ +

yaml_title_replace

+ +

Gispo author_replace

+ +

Version: 2024-09-24

+ +

abstract_replace

+ +
+ +
+

document_title_replace

+
+

Kurssimateriaalien lataaminen

+

Voit ladata harjoituksissa käytettävän kurssihakemiston tästä linkistä:

+ +
+
+

Lukuohje

+

Web-selaimessa suoritettavat komennot on merkitty seuraavasti:

+ +

Komentorivillä suoritettavat komennot on merkitty seuraavasti:

+
+
sudo lsb_release -a
+
+

Tiedostojen sisältö on esitetty seuraavasti:

+
+

#IPv4 local connections: #host all all 127.0.0.1/32 md5

+
+

HTML- ja CSS-koodit, SQL-komennot sekä Python-scriptit on esitetty seuraavasti:

+
+
SELECT version();
+
+

Kysymykset, lisätiedot ja vihjeet on esitetty seuraavasti:

+
+

Mistä löydät tietoa GeoServeristä?

+

GeoServerillä on omat kotisivut.

+

Psst! Tietoa GeoServeristä löydät internetistä.

+
+
+
+

Mistä lisätietoja?

+

Ohessa muutamia web-sivustoja, joista saat lisätietoja:

+ + + + +
+
+

Virheet

+

Jos havaitset materiaalissa virheitä, niin pyydämme ilmoittamaan niistä kouluttajalle tai sähköpostilla .

+
+
+

Lisenssi ja oikeudet

+

Nämä materiaalit on “license_title_replace”- kurssille kehittänyt Gispo Suomi Oy. Materiaalit on lisensoitu seuraavalla lisenssillä: CC BY-ND 4.0

+
+
+

+

+
+
+ +
+ + +
+ +
+ + + + + + + diff --git a/src/GS003/js/answer-key.js b/src/GS003/js/answer-key.js new file mode 100644 index 00000000..cf1c7ecd --- /dev/null +++ b/src/GS003/js/answer-key.js @@ -0,0 +1,110 @@ +var hiddenBoxes = document.getElementsByClassName("hidden-box"); + +const filePath = window.location.pathname; + +const directoryName = filePath.substring(0, filePath.lastIndexOf('/')); + +let language; +if (directoryName.includes("_eng")) { + language = "EN"; +} else if (directoryName.includes("_sv")) { + language = "SV"; +} else { + language = "FI"; +} + +for (var i = 0; i < hiddenBoxes.length; i++) { + hiddenBoxes[i].id = "hidden-box-" + (i + 1); +} + +const codeBoxes = document.querySelectorAll('.code-box, .commandline-box'); + +codeBoxes.forEach(box => { + const copyButton = document.createElement('button'); + copyButton.className = 'copy-button'; + + copyButton.addEventListener('click', () => { + const text = box.innerText.trim(); + + navigator.clipboard.writeText(text) + .then(() => { + copyButton.classList.add('show-checkmark'); + setTimeout(() => { + copyButton.classList.remove('show-checkmark'); + }, 1000); + }) + .catch(err => { + console.error('Failed to copy text:', err); + }); + }); + + box.appendChild(copyButton); +}); + +let show; +let hide; +let next; +let previous; + +switch (language) { + case "EN": + show = "Show "; + hide = "Hide "; + next = "Next"; + previous = "Previous"; + break; + case "SV": + show = "Visa "; + hide = "Dölj "; + next = "Nästa"; + previous = "Tillbaka"; + break; + default: + show = "Näytä "; + hide = "Piilota "; + next = "Seuraava"; + previous = "Edellinen"; +} + +const navButtons = [...document.getElementsByClassName('btn-default')]; +navButtons.forEach(button => { + if (button.textContent === "Next") + { + button.textContent = next; + } + else if (button.textContent === "Previous") + { + button.textContent = previous; + } +}); + +const answerButtons = [...document.getElementsByClassName('answer_btn')]; +answerButtons.forEach(button => { + button.textContent = show + button.textContent; +}); + +function toggleAnswer(button) { + let buttons = document.getElementsByClassName('answer_btn'); + let buttonIndex = Array.from(buttons).indexOf(button) + 1; + + let buttonTextInput = button.textContent; + let words = buttonTextInput.split(" "); + let originalButtonText = words.slice(1).join(' '); + + let boxId = "hidden-box-" + buttonIndex; + let hiddenBox = document.getElementById(boxId); + + if (hiddenBox) { + let currentDisplay = window.getComputedStyle(hiddenBox).getPropertyValue("display"); + if (currentDisplay === "none") { + hiddenBox.style.display = "block"; + button.textContent = hide + originalButtonText; + } else { + hiddenBox.style.display = "none"; + button.textContent = show + originalButtonText; + } + } + else { + button.textContent = "ERROR: Could not find hidden-box"; + } +} diff --git a/src/GS003/js/nav-script.js b/src/GS003/js/nav-script.js new file mode 100644 index 00000000..c4aff29b --- /dev/null +++ b/src/GS003/js/nav-script.js @@ -0,0 +1,74 @@ +function navToggle() { + let sidebar = document.getElementById('sidebar'); + sidebar.classList.toggle('show'); + let hamburger = document.querySelector('.hamburger') + hamburger.classList.toggle('is-active'); +} + +const sidebarLinks = document.querySelectorAll('.sidebar-nav a'); +const documentFileName = window.location.pathname.split('/').pop(); + +var matchingLinks = []; + +sidebarLinks.forEach(function(link) { + var linkHref = link.getAttribute('href').split('#')[0]; + var linkFileName = linkHref.split('/').pop(); + if (linkFileName === documentFileName) + matchingLinks.push(link); + +}); + + +const headers = document.querySelectorAll( + "h1:not(.sidebar-nav h1), " + + "h2, " + + "h3" +); + + +// Jump to to current document's title in the sidebar + +document.addEventListener('DOMContentLoaded', function() { + var pageTitle = document.title; + + var targetLink = null; + + matchingLinks.forEach(function(link) { + if (pageTitle.includes(link.textContent.trim())) + { + targetLink = link; + return; + } + }); + + if (targetLink) { + targetLink.classList.add('in-view'); + targetLink.scrollIntoView({ + behavior: 'instant', + block: 'center' + }); + } +}); + +// Highlight the header in the sidebar you're currently scrolling at in the main body + +window.addEventListener("scroll", () => { + headers.forEach((header) => { + const headerTop = header.offsetTop; + const headerHeight = header.clientHeight; + + if (scrollY >= (headerTop - 50) - headerHeight) + { + current = header; + } + + matchingLinks.forEach((link) => { + link.classList.remove("in-view"); + if (link.textContent == current.textContent) + { + link.classList.add('in-view'); + } + }); + }); +}); + diff --git a/template/custom.html b/template/custom.html index cf990842..0cddb69e 100644 --- a/template/custom.html +++ b/template/custom.html @@ -175,7 +175,7 @@