Skip to content

Commit

Permalink
Add GE01 course
Browse files Browse the repository at this point in the history
  • Loading branch information
JuhoErvasti committed Sep 18, 2024
1 parent 9c8b6aa commit 890a538
Show file tree
Hide file tree
Showing 44 changed files with 6,249 additions and 0 deletions.
1,103 changes: 1,103 additions & 0 deletions src/GE01/00_harjoitus_0.Rmd

Large diffs are not rendered by default.

131 changes: 131 additions & 0 deletions src/GE01/01_harjoitus_1.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Harjoitus 1: Python-konsoli

**Harjoituksen sisältö:** QGISin Python-konsoli ja skriptieditori.

**Harjoituksen tavoite:** Tutustua Python-komentojen ajamiseen konsolissa ja
skriptien ajamiseen skriptieditorissa.

::: note-box
Seuraavissa harjoituksissa oletetaan, että tunnet Python-kielen perusteet.
Tarvittaessa voit kerrata Pythonia [täällä](00_harjoitus_0.html).
:::

Yksinkertaisin tapa käyttää Python-rajapintaa QGISissa on Python-konsolin kautta.
Konsolin avulla voidaan syöttää ja ajaa yksittäisiä komentoja tai pidempiä skriptejä.
Windowsilla QGIS-asennukseen sisältyy erillinen Python-tulkki, jonka versio on 3.12.
Lisäksi asennukseen sisältyy joitain Python-paketteja standardikirjaston lisäksi.
Asennukseen kuuluu myös QGIS-ja gdal/ogr-kirjastot, jotka mahdollistavat eri
komponenttien hyödyntämisen konsolissa ja lisäosissa. Näiden lisäksi asennukseen
kuuluu myös PyQt-kirjasto, eli Qt-käyttöliittymäkirjaston Python-sidonta (binding),
joka mahdollistaa graafisen käyttöliittymän luomisen lisäosille.

::: hint-box
Python toimii käyttöjärjestelmien välillä hieman eri tavalla. Windows- ja
MacOS-käyttöjärjestelmissä QGIS-asennukseen kuuluu oma Python-tulkki, kun
taas Linux-pohjaisilla käyttöjärjestelmillä QGIS käyttää järjestelmän Python-tulkkia.
:::

Python-konsoliin pääsee usealla tavalla (kts. alla oleva kuva):

- Sen voi avata QGISin ylävalikosta Lisäosat > Python-konsoli (Plugins > Python console),
- Pikanäppäinyhdistelmällä Ctrl + Alt + P,
- Klikkaamalla työkalupalkista Python-logon kohdalta

![](img/harjoitus_1/image1.png)

Tarkastellaan konsoli-ikkunan eri osia: vihreästä kolmiosta klikkaamalla komennot
suoritetaan (enter-näppäin ajaa saman asian). Muista ikoneista:

- ![](https://raw.githubusercontent.com/qgis/QGIS/master/images/themes/default/console/iconTabEditorConsole.svg)
Avaa skripti-editorin. Editori helpottaa pidempien skriptien tekoa ja ajamista

- ![](https://raw.githubusercontent.com/qgis/QGIS/master/images/themes/default/console/iconClearConsole.svg)
Tyhjentää konsoli-ikkunan edellisistä komennoista

- ![](https://raw.githubusercontent.com/qgis/QGIS/master/images/themes/default/console/iconHelpConsole.svg)
Täältä löydät kätevästi linkit PyQGIs API -dokumentaatioon, PyQGIS Cookbookiin
sekä konsolin ohjeisiin.

Avaa konsolista skripti-editori ja tutki samoin sen ominaisuuksia.

## Harjoitusten rakenne

## Esimerkit

Eri harjoitusosioissa on annettu esimerkkejä Python-koodista. Suosittelemme,
että käyt esimerkkejä läpi käydessä kopioit esimerkin QGIS-konsolin skriptieditorin
puolelle ja ajat koodin. Esimerkkien yhteydessä voi myös muokata koodia
kokeillakseen eri toimintoja ja lopputuloksia. Esimerkit on annettu
koodilaatikossa, josta sen sisällön voi kopioida viemällä hiiren osoittimen
laatikon päälle ja klikkaamalla laatikon oikeaan yläreunaan ilmestyvää
painiketta.

::: code-box
```python
# Esimerkkikoodia

print("Run me as an example!")
```
:::

## Harjoitukset

Suosittelemme, että työstät myös harjoitusten vastaukset skriptieditorissa.
Harjoitusten yhteydessä on joskus annettu oikeaan suuntaan ohjaava vinkki,
sekä aina malliratkaisu. Saat nämä näkyville painamalla **Näytä ratkaisu** ja
**Näytä vinkki** -painikkeita. Suosittelemme kuitenkin, ettet katso malliratkaisua
liian aikaisin vaan yrität ensin keksiä ratkaisun itse.
Saman asian voi myös tehdä usealla tavalla, ja malliratkaisu on vain yksi
tapa ratkaista ongelma!

## Harjoitus 1.1

Aseta muuttujan a arvoksi 5 Python- konsolissa ja tulosta muuttujan arvo.

<button onclick="toggleAnswer(this)" class="btn answer_btn">vinkki</button>

::: hidden-box
::: code-box
```python
# Aseta ensin arvo
a = 5

# Millä funktiolla voit tulostaa muuttujan arvon?
```
:::
:::

<button onclick="toggleAnswer(this)" class="btn answer_btn">ratkaisu</button>

::: hidden-box
::: code-box
```python
a = 5

print("Muuttujan a arvo", 5)
```
:::
:::

\
\

::: hint-box
Lisävinkkejä konsolin käyttöön:

- Konsoli sopii hyvin myös esimerkiksi PyQGIS-olioiden ominaisuuksien tutkimiseen sekä
testailuun, ja se tukee esimerkiksi PyQGIS-kirjastojen koodintäydennystä (code completion).

- Konsolissa voi myös suorittaa useammalle riville jatkuvia komentoja, kuten for-loopin.
Konsoliin ilmestyy tällöin rivinvaihdon jälkeen kolme pistettä sen merkiksi, että se
odottaa lisäkomentoja. Huomaa kuitenkin, että pisteet eivät vastaa Python-koodin
sisennystä, vaan käyttäjän on huolehdittava tästä.

- Nuolinäppäimillä ylös- ja alaspäin pääsee siirtymään konsoliin syötettyjen komentojen
historiassa.

- Konsolipaneeli aukeaa todennäköisesti karttaikkunan alapuolelle, mutta sen pystyy
raahaamaan ja telakoimaan haluttaessa muuallekin QGISin käyttöliittymässä tai irrottamaan
omaksi ikkunakseen.
:::

226 changes: 226 additions & 0 deletions src/GE01/02_harjoitus_2.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# Harjoitus 2: PyQGIS-perusteet

**Harjoituksen sisältö:** QGISin Python-paketin käytön perusteet

**Harjoituksen tavoite:** Harjoituksen jälkeen opiskelija hallitsee PyQGISin perusteet.

QGISin Python-konsolissa on käytössä valmiiksi alustettu `iface`-muuttuja,
joka on QgisInterface-luokan olio. Tämä olio mahdollistaa mm. vuorovaikutuksen
tällä hetkellä käynnissä olevan QGIS-ohjelman eri komponenttien kanssa.
Sen kautta voidaan manipuloida esimerkiksi projektiin ladattuja
tasoja tai vaikka pääkarttanäkymää (QgsMapCanvas).

## Tasojen käsittely

Avaa QGISiin kurssihakemistosta `NaturalEarth_10m_admin_0_countries.gpkg`-tiedosto.
Klikkaa se aktiiviseksi tasopaneelissa.

Valitaan aktiivinen taso:

::: code-box
```python
layer = iface.activeLayer()
print(layer)
```
:::

Haetaan vektoritason kohteiden määrä:

::: code-box
```python
layer = iface.activeLayer()
feature_count = layer.featureCount()

print(feature_count)
```
:::

Iteroidaan vektoritasojen kohteita ja tulostetaan
ominaisuustieto:

::: code-box
```python
layer = iface.activeLayer()

for feature in layer.getFeatures():
attribute = feature.attribute('NAME_EN')

print(attribute)
```
:::

Lisää QGISiin selainpaneelista **XYZ Tiles**-valikon alta
**OpenStreetMap**-taso.

Haetaan listaus kaikista projektin tasoista:

::: code-box
```python
layers = QgsProject.instance().mapLayers()

# Tarkistetaan layers-muuttujan tyyppi
print(type(layers))
```
:::

Tässä `mapLayers()`-metodi palauttaa `dictionary`-
datatyypin, jossa avaimena on tasolle oma uniikki tunniste
merkkijonona ja arvona on varsinainen taso-olio.

::: hint-box
Tässä esimerkissä meillä on projektissa vektori- ja rasteritaso.
Näiden olioiden luokat on QgsVectorLayer ja QgsRasterLayer.
Huomaa, että nämä luokat perivät
[QgsMapLayer](https://qgis.org/pyqgis/master/core/QgsMapLayer.html)-luokan,
jolloin niillä on myös yhteisiä metodeja.
:::

Iteroidaan tasoja:

::: code-box
```python
# Voidaan käyttää listakoostetta
# (kts. tarvittaessa Python-lisätehtävistä
# aihe 1).

# Tason ID:tä ei välttämättä tarvi,
# joten voidaan hakea suoraan vain
# tasot
layers = [layer for layer in QgsProject.instance().mapLayers().values()]

for layer in layers:
print(f"name: {layer.name()}")
```
:::

Valitaan taso nimen perusteella:

::: code-box
```python
layers = QgsProject.instance().mapLayersByName('OpenStreetMap')

# mapLayersByName() palauttaa listan
# tasoista, koska QGISissa on sallittua
# olla useampi taso samalla nimellä

layer = layers[0]

print(layer.name())
```
:::


::: hint-box
**QgsProject**-luokka noudattaa ns. singleton (ainokainen)
suunnittelumallia, joka tarkoittaa sitä että luokasta
voi olla kerrallaan olemassa ainoastaan yksi olio. Tämä
olio voidaan hakea käyttämällä QgsProject-luokan
staattista metodia `instance()`, joka palauttaa aina saman olion.
Koska metodi on staattinen projektiolioon on globaali pääsy.
:::

## Harjoitus 2.1

Selvitä Pythonilla **OpenStreetMap**-tason datan lähde.
Tutki tarvittaessa [PyQGIS-dokumentaatiota](https://qgis.org/pyqgis/latest/).

<button onclick="toggleAnswer(this)" class="btn answer_btn">ratkaisu</button>

::: hidden-box
::: code-box
```python
layers = QgsProject.instance().mapLayersByName('OpenStreetMap')
layer = layers[0]

data_source = layer.source()
print(data_source)
```
:::

Tulos:

```
type=xyz&url=https://tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0
```
:::

## Harjoitus 2.2

Kirjoita skripti, joka käy läpi kaikki projektiin lisätyt tasot
ja tulostaa **vain** vektoritasojen kenttien nimet.

<button onclick="toggleAnswer(this)" class="btn answer_btn">vinkki</button>

::: hidden-box
::: code-box
```python
layers = [layer for layer in QgsProject.instance().mapLayer().values() <ehtolause>] # miten tarkistat onko kyseessä vektoritaso?

for layer in layers:
fields = <funktio> # miten saat QgsFields-olion tasosta?

for field in fields:
<tulosta nimi>
```
:::
:::


<button onclick="toggleAnswer(this)" class="btn answer_btn">ratkaisu</button>

::: hidden-box
::: code-box
```python
layers = [layer for layer in QgsProject.instance().mapLayers().values() if layer.type() == QgsMapLayer.VectorLayer]

for layer in layers:
fields = layer.fields()

print(f"layer '{layer.name()}' fields:")

for field in fields:
print(field.name())
```
:::
:::

## Harjoitus 2.3

Kirjoita skripti, joka tulostaa kaikkien Etelä-Amerikan
alueiden nimet **NaturalEarth**-tasolta. Valitse maanosa
`CONTINENT`-sarakkeen perusteella.

<button onclick="toggleAnswer(this)" class="btn answer_btn">vinkki</button>

::: hidden-box
::: code-box
```python
# Hae taso esim. nimen perusteella
layers = <funktio>

layer = layers[0]

for feature in layer.getFeatures():
# Millä metodilla haet kohteen ominaisuustiedon?
# Vertaa merkkijonoon
if <funktio> == "South America":
print(<kohteen nimi>)
```
:::
:::

<button onclick="toggleAnswer(this)" class="btn answer_btn">ratkaisu</button>

::: hidden-box
::: code-box
```python
layers = QgsProject.instance().mapLayersByName('NaturalEarthAdmin0')

layer = layers[0]

for feature in layer.getFeatures():
if feature.attribute("CONTINENT") == "South America":
print(feature.attribute("SOVEREIGNT"))
```
:::
:::
Loading

0 comments on commit 890a538

Please sign in to comment.