GRASS GIS / Poznámky pro družicová data Landsat: Porovnání verzí

Z GeoWikiCZ
m (fig)
 
(Není zobrazeno 276 mezilehlých verzí od stejného uživatele.)
Řádek 1: Řádek 1:
{{Upravit}}
Tato stránka obsahuje poznámky pro stažení dat [http://en.wikipedia.org/wiki/Landsat_program LandSat] [http://en.wikipedia.org/wiki/Thematic_Mapper Thematic Mapper] a následné zpracování těchto dat v prostřední [[GRASS GIS]]. Více v rámci předmětu [[153ZODH|Zpracování obrazových dat]].
 
Tato stránka obsahuje poznámky pro hromadné stažení dat [http://en.wikipedia.org/wiki/Landsat_program LandSat] [http://en.wikipedia.org/wiki/Thematic_Mapper Thematic Mapper] a nasledné zpracování těchto dat v prostřední [[GRASS GIS]].
 
; Časová řada
 
* 80. léta (co nejstarší)
* přelom 80. a 90. let
* kolem roku 2000
* nejnovější
 
 
__TOC__
__TOC__
== Postup pro manuální stažení dat ==
== Postup pro manuální stažení dat ==
Řádek 28: Řádek 17:
== Import dat ==
== Import dat ==


Data je nejprve nutno dekomprimovat
Data je nejprve nutno dekomprimovat, např.


  tar xvzf LT51910252009213MOR00.tar.gz
  tar xvzf LT51910252009213MOR00.tar.gz


Archiv obsahuje několik souborů ve formátu [http://en.wikipedia.org/wiki/Geotiff geotiff] (jeden soubor na kanál), seznam identických bodů (GCP, ground control points) a metadata v textové podobě (MTL).
Příklad skriptu pro [[Bash]], který rozbalí všechny dostupné archivy v aktuálním adresáři (pro každý rozbalený archiv vytvoří samostatný adresář).
 
<source lang="bash">
#!/bin/sh
 
for file in *.tar.gz ; do
    echo "Zpracovavam $file..."
    dir=${file%.tar.gz}
    if [ -d $dir ] ; then
        rm -rf $dir
    fi
    mkdir $dir
    tar -xzf $file -C $dir
done
</source>
 
Archiv obsahuje několik souborů ve formátu [http://en.wikipedia.org/wiki/Geotiff geotiff] (jeden soubor na kanál), seznam identických bodů (GCP, ground control points), metadata v textové podobě (MTL) a soubor README.GTF.


  ls -w1
  ls -w1
Řádek 104: Řádek 109:
=== Hromadný import dat ===
=== Hromadný import dat ===


Data lze naimportovat pomocí modulu {{GrassPrikaz|r.in.gdal}} (<tt>File -> Import raster data -> Import raster data</tt>), ukázka z příkazové řadky (přepínač <tt>-e</tt> nastavní po importu region podle vstupní dat).
Data lze naimportovat pomocí modulu {{GrassPrikaz|r.in.gdal}} (<tt>File -> Import raster data -> Common import formats</tt>), ukázka z příkazové řadky (přepínač <tt>-e</tt> nastaví po importu region podle vstupní dat).


  r.in.gdal -e input=L5191025_02520090801_B10.TIF output=b10 title="kanal 1"
  r.in.gdal -e input=L5191025_02520090801_B10.TIF output=b10 title="kanal 1"
Řádek 141: Řádek 146:
</pre>
</pre>


Data lze hromadně naimportovat pomocí jednoduchého skriptu.
Data lze hromadně naimportovat pomocí jednoduchého skriptu. Následuje ukázka pro [[Bash]] a [[Python]].


==== Bash ====
==== Bash ====


Úkázka pro [[Bash]].
Ukázka skriptu pro [[Bash]] (pro každou družicovou scénu vytvoří vlastní mapset).
 
<source lang="bash">
#!/bin/sh
function import()
{
    idx=1
    for file in `ls *.TIF 2>/dev/null`; do
        oname=`echo $file | cut -d'_' -f3 | cut -d'.' -f1`
oname=${oname:0:2}
        g.message "Importuji $file -> $oname@$mapset..."
        g.mapset -c mapset=$mapset --quiet 2>/dev/null
        r.in.gdal -e input=$file output=$oname title="kanal $idx" --overwrite --quiet
        idx=$(($idx+1))
    done
}
eval `g.gisenv`
if test -n "$1" ; then
    mapset=$1
    cd $1
    import
else
    for dir in `find . -maxdepth 1 -mindepth 1 -type d`; do
mapset=`echo $dir | cut -d'/' -f2`
cd $dir
import
cd ..
    done
fi
g.mapset mapset=$MAPSET --quiet
</source>
 
Příklad spuštění:
 
* <tt>./import.sh</tt> projde všechny adresáře
* <tt>./import.sh LM41890261983200FFF03</tt> naimportuje data pouze z vybraného adresáře


==== Python ====
==== Python ====


Úkázka pro [[Python]].
Ukázka skriptu pro [[Python]] (pro každou družicovou scénu vytvoří vlastní mapset). Tento skript navíc nastaví timestamp rastrových dat.
 
<source lang="python">
#!/usr/bin/python
import os
import sys
import glob
import grass.script as grass
def get_timestamp(mapset):
    try:
        metafile = glob.glob(mapset + '/*MTL.txt')[0]
    except IndexError:
        return
    result = dict()
    try:
        fd = open(metafile)
        for line in fd.readlines():
            line = line.rstrip('\n')
            if len(line) == 0:
                continue
            if 'ACQUISITION_DATE' in line:
                result['date'] = line.strip().split('=')[1].strip()
    finally:
        fd.close()
    return result
def import_tifs(mapset):
    meta = get_timestamp(mapset)
    for file in os.listdir(mapset):
        if os.path.splitext(file)[-1] != '.TIF':
            continue
        ffile = os.path.join(mapset, file)
        name = os.path.splitext(file)[0].split('_')[-1] #B10, B61
        if name[-1] == '0':
            name = name[:2]
        kanal = int(name[1]) # B
        grass.message('Importuji %s -> %s@%s...' % (file, name, mapset))
        grass.run_command('g.mapset',
                          flags = 'c',
                          mapset = mapset,
                          quiet = True,
                          stderr = open(os.devnull, 'w'))
        grass.run_command('r.in.gdal',
                          input = ffile,
                          output = name,
                          quiet = True,
                          overwrite = True,
                          title = 'kanal %d' % kanal)
        if meta:
            year, month, day = meta['date'].split('-')
            if month == '01':
                month = 'jan'
            elif month == '02':
                month = 'feb'
            elif month == '03':
                month = 'mar'
            elif month == '04':
                month = 'apr'
            elif month == '05':
                month = 'may'
            elif month == '06':
                month = 'jun'
            elif month == '07':
                month = 'jul'
            elif month == '08':
                month = 'aug'
            elif month == '09':
                month = 'sep'
            elif month == '10':
                month = 'oct'
            elif month == '11':
                month = 'nov'
            elif month == '12':
                month = 'dec'
            grass.run_command('r.timestamp',
                              map = name,
                              date = ' '.join((day, month, year)))
def main():
    if len(sys.argv) == 1:
        for directory in filter(os.path.isdir, os.listdir(os.getcwd())):
            import_tifs(directory)
    else:
        import_tifs(sys.argv[1])
if __name__ == "__main__":
    main()
</source>


==== wxGUI ====
==== wxGUI ====
Dále lze data naimportovat pomocí wxGUI.
Dále lze data naimportovat pomocí {{grassPrikaz|wxGUI}}.
 
File -> Import raster data -> Common formats import
 
<center>
<gallery widths=400px heights=300x perrow=3>
Image:import-tm-0.png
Image:import-tm-1.png
</gallery>
</center>


== Seznam měst ==
=== Vytvoření přehledky ===
 
Následující skript v jazyce [[Python]] vytvoří vektorovou přehledku včetně atributové tabulky s názvy mapsetů.
 
<source lang=python>
#!/usr/bin/python
 
import os
import sys
import grass.script as grass
 
maps = [] # tmp maps
cats = {} # attribute table
cat = 1
 
os.environ['GRASS_VERBOSE'] = '0'
os.environ['GRASS_OVERWRITE'] = '1'
 
# list mapsets
for mapset in grass.mapsets():
    if mapset[:2] not in ('LE', 'LM', 'LT'):
        continue
    grass.run_command('g.region',
                      rast = "B1@%s" % mapset)
    grass.run_command('v.in.region',
                      output = mapset,
                      type = 'area',
                      cat = cat)
    cats[cat] = mapset
    maps.append(mapset)
   
    cat += 1
 
# patch maps
grass.run_command('v.patch',
                  input = ','.join(maps),
                  output = 'prehledka')
 
# create attribute table
grass.run_command('v.db.addtable',
                  map = 'prehledka',
                  columns = 'cat integer, mapset varchar(255)')
for cat, mapset in cats.iteritems():
    grass.run_command('v.db.update',
                      map = 'prehledka',
                      column = 'mapset',
                      value = mapset,
                      where = "cat = %d" % cat)
 
for m in maps:
    grass.run_command('g.remove',
                      quiet = True,
                      vect = m)
</source>
 
[[Image:landsat-prehledka.png|center|640px|thumb|Vektorová přehledka mapsetů s importovanými daty Landsat]]
 
=== Test lokace ===
 
Následující skript v jazyce [[Python]] vypíše pro danou lokaci (město) relevantní mapsety včetně roku, ve kterém byly snímky Landsat pořízeny.
 
<source lang=python>
#!/usr/bin/python
 
#%Module
#% description: Vypis mapsety pro danou lokaci
#%End
#%Option
#% key: mesto
#% description: Nazev mesta (bez diakritiky)
#% type: string
#% required: yes
#%End
 
import os
import sys
import atexit
 
import grass.script as grass
 
tmp_maps = []
def cleanup():
    grass.run_command('g.remove',
                      vect = '%s' % ','.join(tmp_maps))
   
def main():
    os.environ['GRASS_VERBOSE'] = '0'
    os.environ['GRASS_OVERWRITE'] = '1'
   
    grass.run_command('v.extract',
                      input = 'obce',
                      output = 'obce_tmp',
                      where = "nazev_eng = '%s'" % options['mesto'])
    tmp_maps.append('obce_tmp')
   
    mapsets = []
   
    # list mapsets
    for mapset in grass.mapsets():
        if mapset[:2] not in ('LE', 'LM', 'LT'):
            continue
        grass.run_command('g.region',
                          rast = "B1@%s" % mapset)
        grass.run_command('v.in.region',
                          output = mapset,
                          type = 'area')
        tmp_maps.append(mapset)
       
        grass.run_command('v.select',
                          flags = 't',
                          ainput = mapset,
                          atype = 'area',
                          binput = 'obce_tmp',
                          btype = 'area',
                          output = 'mapset_tmp')
       
        if bool(grass.vector_info_topo('mapset_tmp')['areas']):
            ret = grass.read_command('r.timestamp',
                                    map = 'B1@%s' % mapset)
            year = ''
            if ret:
                year = ret.split(' ')[-1].strip()
           
            print mapset, year
   
        grass.run_command('g.remove',
                          vect = 'mapset_tmp')
   
    return 0
 
if __name__ == "__main__":
    atexit.register(cleanup)
    options, flags = grass.parser()
    sys.exit(main())
</source>
 
Příklad:
 
test.py mesto='Ceske Budejovice'


<pre>
<pre>
Brno                                               
LE71900262010185ASN00 2010
Břeclav                                           
LM41910261984105FFF03 1984
Česká Lípa                                     
LT41910261992191XXX02 1992
Česká Třebová                                   
LT51900261991173XXX02 1991
České Budějovice                             
LT51900262002171MTI00 2002
Český Těšín                                   
LT51900262010193MOR00 2010
Děčín                                             
LT51910262002178MTI00 2002
Frýdek-Místek                       
LT51910262010184MOR00 2010
Cheb                                             
LT51920262002137MTI00 2002
Chomutov                                     
Chrudim                                       
Jablonec                                       
Jablonec nad Nisou                               
Jihlava                                           
Jirkov                                           
Kadaň                                           
Karviná                                         
Klatovy                                   
Kutná Hora                                         
Liberec                                               
Litvínov                                           
Náchod                                   
Ostrava
Plzeň
Přerov
Příbram
Rožnov pod Radhoštěm
Sokolov
Teplice
Třebíč
Ústí nad Labem
Ústí nad Orlicí
Valašské Meziříčí
Vsetín
Zlín
Znojmo
Žďár nad Sázavou
</pre>
</pre>
== Předzpracování dat (no-data) ==
Po importu dat je vhodné nahradit hodnotu '0' za 'null' (žádná data, no-data) - {{GrassPrikaz|r.null}}.
g.copy rast=B1@ceske-budejovice-2000,B1
g.region rast=B1
r.null map=B1 setnull=0
Tuto operaci lze provést hromadně pro všechny kanály družicové scény z vybraného mapsetu (více k tématu skriptování v jazyce [[Python]] na portálu {{freegis|GRASS GIS / Skriptování|FreeGIS}}).
<source lang="python">
#!/usr/bin/python
from grass.pygrass.gis import Mapset
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster  as r
mapset = 'ceske-budejovice-2000'
   
for band in sorted(Mapset(mapset).glist('rast')):
    g.message("Kopiruji %s..." % band)
    g.copy(rast = '%s@%s,%s' % (band,mapset,band))
    g.region(rast = band)
    g.message("Nastavuji no-data pro %s..." % band)
    r.null(map = band, setnull='0')
</source>
Další možností je vytvořit ''model'' pomocí {{GrassPrikaz|g.gui.gmodeler|grafického modeleru}} [[Image:wxgui-modeler.png]] {{menu|File|Graphical Modeler}}
Příklad modelu - pro každý kanál
# vytvoří kopii kanálu v aktuálním mapsetu (nutné pro {{GrassPrikaz|r.null}})
# nastaví region
# nahradí hodnotu 0 hodnotou NULL
{{YouTube|LFdn1VozMfo|desc=Model pro předzpracování družicových dat (hodnoty NULL)}}
{{fig|grass-rgb-null|Porovnání barevné syntézy ve skutečných barvách s hodnotami 0 a NULL|size=640}}
== Nastavení vyhledávací cesty ==
Mapset s naimportovanými daty přidáme do vyhledávací cesty pomocí {{GrassPrikaz|g.mapsets}}
g.mapsets mapset=LM41890261983200FFF03 operation=add
nebo pomocí {{GrassPrikaz|wxGUI}}
Settings -> GRASS working environment -> Mapset access
[[Image:wxgui-mapset-access.png|center|thumb|300px|Nastavení přístupu k mapsetům]]
== Vizualizace dat ==
[[Image:wxgui-tm453.png|center|640px|thumb|Příklad vizualizace kompozitního snímku (kanály 453)]]
== Zájmové území ==
Nejprve vytvoříme vektorovou vrstvu s polygonem zájmového města (v tomto případě 'Pardubice' - název města zadejte bez diakritiky).
v.extract input=obce output=par where="nazev_eng = 'Pardubice'
Výpočetní region nastavíme pomocí {{GrassPrikaz|g.region}}, např. pro město "Pardubice" (rastrovou vrstvu zadáváme pro nastavení prostorového rozlišení) s offsetem 10km.
g.region rast=B1 vect=par n=n+1e4 s=s-1e4 w=w-1e4 e=e+1e4 save=par
[[Image:grass-region-par.png|center|640px|thumb|Nastavení výpočetního regionu na základě vektorové vrstvy s daným offsetem]]
Opětovné nastavení výpočetního regionu
g.region region=par
<!--
Dále mvytvoříme masku zájmového území vybraného města.
v.to.rast input=Vsetin output=MASK use=val value=1
[[Image:grass-region-mask.png|center|640px|thumb|Nastavení masky zájmové oblasti]]
-->
== Interpolace chybějících dat ==
Pro interpolaci chybějících hodnot lze použít modul {{GrassPrikaz|r.fillnulls}}.
g.region rast=B1
r.fillnulls input=B1 output=B1_fill
== Atmosferické korekce ==
Další poznámky najdete na [http://grass.osgeo.org/wiki/Atmospheric_correction GRASS Wiki].
Provedeme konverzi digitálních hodnot (DH) na hodnoty záření na vrcholu atmosféry ({{wikipedia|Atmosphere of Earth|TOA|lang=en}}) pomocí modulu {{GrassPrikaz|i.landsat.toar}} (soubory s metadaty jsou dostupné na adrese: http://geo102.fsv.cvut.cz/zodh/metadata/).
<source lang="bash">
i.landsat.toar -r input_prefix=Brg output_prefix=B_rad \
metfile=/work/geodata/zodh2010/data/jablonec-nad-nisou-1980/L4191025_02519840414_MTL.txt
</source>
Připravíme konfigurační soubor pro {{GrassPrikaz|i.atcorr}}. Tento modul aplikuje atmosferické korekce, přepočte hodnoty záření na vrcholu atmosféry na hodnoty absolutní odrazivosti objektů.
Některé údaje získáme z metadatového souboru (MTL), jiné spočteme.
* Dat a čas pořízení dat
<source lang=python>
cd /work/geodata/zodh2010/data/jablonec-nad-nisou-1980/
grep -a 'ACQUISITION_DATE' L4191025_02519840414_MTL.txt
</source>
<pre>
    ACQUISITION_DATE = 1984-04-14
</pre>
<source lang=python>
grep -a 'SCENE_CENTER_SCAN_TIME' L4191025_02519840414_MTL.txt
</source>
<pre>
    SCENE_CENTER_SCAN_TIME = 09:17:32.4240000Z
</pre>
* Střed scény v zeměpisných souřadnicích
<source lang="bash">
g.region -lg
</source>
<pre>
...
center_long=15.16068438
center_lat=50.72648033
...
</pre>
nebo
<source lang="bash">
g.region -c
</source>
<pre>
center easting:  511341.280354
center northing: 5619440.568602
</pre>
<source lang="bash">
echo '511341|5619440' | m.proj -ogd --q
</source>
<pre>
15.16068045|50.72664675
</pre>
Poznámka: V prvním případě se používá elipsoid lokace, v druhém WGS84.
* Průměrná výška
<source lang="bash">
r.univar map=dem --q | grep 'mean:'
</source>
<pre>
mean: 552.711
</pre>
Příklad konfiguračního souboru:
<pre>
7                                    # geometricke podminky Landsat TM
04 14 9.28 15.16068045 50.72664675  # mesic den h.mmm (GMT) zem. sirka delka
2                                    # atmosfericky model midlatitude summer
3                                    # aerosol model urban
50                                  # viditelnost [km]
-0.552                              # prumerna vyska [km]
-1000                                # sensor na urovni nosice
31                                  # 1. kanal Landsat 5 TM
</pre>
Atmosferické korekce nakonec aplikujeme příkazem
<source lang="bash">
i.atcorr iimg=B_rad1 icnd=atcorr.txt oimg=B_cor1
</source>
== Detekce oblačnosti ==
Detekovat mraky (a jejich teplotu) umožňuje modul {{GrassPrikaz|i.landsat.acca}}.
Nejprve konvertuje DH na hodnoty odrazivosti (soubory s metadaty (MTL) jsou dostupné [http://geo102.fsv.cvut.cz/zodh/metadata/ zde]).
<source lang="bash">
i.landsat.toar input_prefix=Brg output_prefix=B_ref metfile=L4191025_02519840414_MTL.txt
</source>
Provedeme výpočet (pouze pro TM/ETM+)
<source lang="bash">
i.landsat.acca -5 input_prefix=B_ref output=B_acca
</source>
<source lang="bash">
r.report map=B_acca units=p
</source>
<pre>                                             
+-----------------------------------------------------------------------------+
|                        RASTER MAP CATEGORY REPORT                          |
|LOCATION: zod2010                                    Mon Nov  8 23:05:41 2010|
|-----------------------------------------------------------------------------|
|          north: 5633223.50868776    east: 525403.19584718                  |
|REGION    south: 5605657.62851621    west: 497279.36486102                  |
|          res:        29.99551705    res:      30.01476092                  |
|-----------------------------------------------------------------------------|
|MASK:none                                                                    |
|-----------------------------------------------------------------------------|
|MAP: LANDSAT-5 TM Automatic Cloud Cover Assessment (B_acca@landa in landa)  |
|-----------------------------------------------------------------------------|
|                        Category Information                          |  %  |
|#|description                                                        | cover|
|-----------------------------------------------------------------------------|
|6|Cold cloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . .|  0.01|
|9|Warm cloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . .|  0.10|
|*|no data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .| 99.89|
|-----------------------------------------------------------------------------|
|TOTAL                                                                |100.00|
+-----------------------------------------------------------------------------+
</pre>
=== Nastavení masky ===
Příklad nastavení masky oblačnosti pomocí {{GrassPrikaz|r.mask}}
<source lang=bash>
r.mask -i input=B_acca maskcats="6 9"
</source>
[[Image:B321.png|thumb|center|640px|Barevná syntéza 321]]
[[Image:B_acca.png|thumb|center|640px|Výsledek detekce oblačnosti]]
[[Image:B321_cloud.png|thumb|center|640px|Barevná syntéza 321 s maskou oblačnosti]]
== Výpočet pozice slunce ==
Pozici slunce můžeme určit pomocí {{GrassPrikaz|r.sunmask}}. Nejprve z metadat určíme vstupní parametry.
<source lang=python>
cd /work/geodata/zodh2010/data/jablonec-nad-nisou-1980/
grep -a 'ACQUISITION_DATE' L4191025_02519840414_MTL.txt
</source>
<pre>
    ACQUISITION_DATE = 1984-04-14
</pre>
<source lang=python>
grep -a 'SCENE_CENTER_SCAN_TIME' L4191025_02519840414_MTL.txt
</source>
<pre>
    SCENE_CENTER_SCAN_TIME = 09:17:32.4240000Z
</pre>
<source lang=python>
r.sunmask -sg elevation=dem year=1984 month=4 day=14 hour=9 minute=17 second=32 timezone=0
</source>
<pre>
sunazimuth=143.930862
sunangleabovehorizon=44.188877
</pre>
Vypočtena data můžeme porovnat s hodnotami uvedenými v metadatovém souboru.
<source lang=python>
grep -a 'SUN_' L4191025_02519840414_MTL.txt
</source>
<pre>
    SUN_AZIMUTH = 143.8553375
    SUN_ELEVATION = 44.1660847
</pre>
== Klasifikace dat ==
=== Neřízená klasifikace ===
Více na stránkách [[153ZODH / 10. cvičení#Neřízená klasifikace|10. cvičení]] předmětu [[153ZODH|Zpracování obrazových dat]].
=== Řízená klasifikace ===
Více na stránkách [[153ZODH / 11. cvičení|11. cvičení]] předmětu [[153ZODH|Zpracování obrazových dat]].
Pokud máme k dispozici výsledky [[#Detekce oblačnosti|analýzy oblačnosti]], muže je použít pro vytvoření masky
<source lang="bash">
r.mask -i input=B_acca maskcats="6 9"
</source>
Nejprve vytvoříme obrazovou skupinu (předpokládejme, že <tt>Brg</tt> jsou obrazové kanály po [[GRASS GIS - Poznámky pro družicová data Landsat#Atmosferické korekce|atmosferické korekci]])
<source lang="bash">
i.group group=Brg subgroup=Brg input=Brg1,Brg2,Brg3,Brg4,Brg5,Brg7
</source>
nebo pomocí dialogu {{grassPrikaz|wxGUI}} {{menu|Imagery|Develop images and groups|Create/edit group}}
<center>
{|table
| [[Image:group-dialog-1.png|thumb|400px|Založení obrazové skupiny ve wxGUI (výběr rastrových map)]]
| [[Image:group-dialog-2.png|thumb|185px|Založení obrazové skupiny ve wxGUI]]
|}
</center>
Barevnou kompozici lze vytvořit pomocí {{GrassPrikaz|r.composite}}
<source lang="bash">
g.region rast=Brg4
r.composite red=Brg4 green=Brg5 blue=Brg3 output=Brg453
</source>
Provedeme digitalizaci vybraných trénovacích ploch, viz cvičení předmětu [[153YZOD Zpracování obrazových dat - cvičení 11#Neinteraktivní|Zpracování obrazových dat]]. Trénovací plochy definujeme pro následující třídy:
* Zástavba
* Nelesní zeleň
* Lesní porosty
* Zemědělská půda
* Vodní plochy
Digitalizovat trénovací plochy lze ve specializovaném nástroji {{grassPrikaz|g.gui.iclass}} (více [[153ZODH / 11. cvičení#Supervised Classification Tool (nový nástroj, GRASS 7)|zde]]). Anebo nepřímo pomocí {{grassPrikaz|wxGUI.vdigit|digitalizačního nástroje}} - viz návod níže.
{{YouTube|v7f07jzt5uo|desc=Vytvoření vektorové vrstvy trénovacích ploch, digitalizace ploch lesních porostů, vodních ploch a zástavby}}
<center>
{|table
| [[Image:grass-attrb.png|thumb|400px|center|Atributová tabulka vektorové vrstvy trénovacích ploch]]
<!--[[Image:grass-digit-settings.png|thumb|640px|center|Nastavení digitizéru při vektorizaci trénovacích ploch]]-->
| [[Image:grass-digit-tp.png|thumb|400px|center|Atributy vektorové vrstvy trénovacích ploch]]
|}
</center>
Po přidání atributového sloupce GRASSRGB ({{GrassPrikaz|v.db.addcolumn}}) můžeme pro vektorovou vrstvu trénovacích ploch nastavit tabulku barev.
<source lang="bash">
v.db.addcol map=tp column="GRASSRGB varchar(11)"
</source>
Vector -> Manage colors -> Color rules
{{fig|grass-vcolors|Nastavení tabulky barev vektorové vrstvy trénovacích ploch}}
Pro aktivaci tabulky barev použijeme přepínač <code>-a</code> modulu {{GrassPrikaz|d.vect}}
<source lang="bash">
d.vect -a map=tp
</source>
Dále provedeme rasterizaci trénovacích ploch
<source lang="bash">
v.to.rast input=tp type=area output=tp use=attr column=cat rgbcolumn=GRASSRGB labelcolumn=trida
</source>
[[Image:grass-tp453.png|thumb|640px|center|Rasterizované trénovací plochy, na pozadí barevná kompozice RGB 453]]
A na závěr klasifikaci dat
* pomocí klasifikátoru MLC ({{GrassPrikaz|i.gensig}}, {{GrassPrikaz|i.maxlik}})
<source lang="bash">
i.gensig trainingmap=tp group=Brg subgroup=Brg signaturefile=sig
i.maxlik group=Brg subgroup=Brg sigfile=sig class=Brg_mlc reject=Brg_mlc_ref
</source>
* pomocí klasifikátoru SMAP ({{GrassPrikaz|i.gensigset}}, {{GrassPrikaz|i.smap}})
<source lang="bash">
i.gensigset trainingmap=tp group=Brg subgroup=Brg signaturefile=sig_smap
i.smap group=Brg subgroup=Brg signaturefile=sig_smap output=Brg_smap
</source>
Můžeme převzít tabulku barev pomocí příkazu
<source lang="bash">
r.colors map=Brg_mlc raster=tp
r.colors map=Brg_smap raster=tp
</source>
[[Image:grass-mlc.png|thumb|640px|center|Výsledek klasifikace (MLC)]]
[[Image:grass-smap.png|thumb|640px|center|Výsledek klasifikace (SMAP)]]
==== Využití dat z projektu OpenStreetMap ====
Pro určení trénovacích ploch lze také využít tématické vrstvy z mapsetu [http://geo102.fsv.cvut.cz/zodh/grassdata/base/osm.zip osm] vytvořeného z dat {{freegis|Import dat OpenStreetMap do GRASS GIS|OpenStreetMap}}. Příklad vytvoření pomocných tématických vrstev (viz [http://wiki.openstreetmap.org/wiki/CS:Map_Features#Vyu.C5.BEit.C3.AD_krajiny_.28Landuse.29 wiki OSM]):
<source lang=bash>
v.extract input=landuse output=landuse_zastavba where="type = 'brownfield' or type = 'cemetery' or type = 'commercial' \
  or type = 'construction' or type = 'farmyard' or type = 'garages' or type = 'industrial' or type = 'residential' \
  or type = 'retail'" new=1
v.extract input=landuse output=landuse_zelen where="type = 'allotments' or type = 'grass' or type = 'greenfield' or \
  type = 'greenhouse_horticulture' or type = 'recreation_ground' or type='village_green' or type = 'park'" new=2
v.extract input=natural output=landuse_les where="type = 'forest'" new=3
v.extract input=landuse output=landuse_zemedel where="type = 'farm' or type = 'farmland' or type = 'orchard' \
  or type = 'vineyard'" new=4
v.extract input=landuse output=landuse_voda where="type = 'reservoir'" new=5
</source>
Alternativně můžeme vytvořit "reklasifikovanou" landuse rastrovou mapu:
<source lang=bash>
v.proj location=osm_shp mapset=PERMANENT input=landuse_zastavba
v.proj location=osm_shp mapset=PERMANENT input=landuse_zelen
v.proj location=osm_shp mapset=PERMANENT input=landuse_les
v.proj location=osm_shp mapset=PERMANENT input=landuse_zemedel
v.proj location=osm_shp mapset=PERMANENT input=landuse_voda
g.region rast=B1@beroun-2000 vect=cr
v.to.rast input=landuse_zastavba output=landuse_zastavba type=area use=val value=1
v.to.rast input=landuse_zelen output=landuse_zelen type=area use=val value=2
v.to.rast input=landuse_les output=landuse_les type=area use=val value=3
v.to.rast input=landuse_zemedel output=landuse_zemedel type=area use=val value=4
v.to.rast input=landuse_voda output=landuse_voda type=area use=val value=5
r.patch input=landuse_zastavba,landuse_zelen,landuse_les,landuse_zemedel,landuse_voda out=landuse_tridy
r.colors map=landuse_tridy rules=- << EOF
1 139:105:20
2 59:227:59
3 7:121:7
4 191:191:191
5 0:0:255
nv 255:255:255
default 255:255:255
EOF
</source>
[[Image:osm-landuse.png|center|640px|thumb|Landuse dle OSM (zástavba - hnědá, zemědělská půda - šedivá, lesní porosty - tmavě zelená, zeleň - zelená, vodní plochy - modrá)]]
[[Image:landuse-tm453.png|center|640px|thumb|Landuse (zástavba - hnědá, zemědělská půda - šedivá, lesní porosty - tmavě zelená, zeleň - zelená, vodní plochy - modrá), hranice města (červená), na pozadí barevná kompozice 453]]
==== Využití dat z veřejně dostupných WMS ====
Jako podkladovou vrstvu můžeme také použít data z WMS serveru
http://www.bnhelp.cz/ows/crtopo
Seznam dostupných vrstev získáme příkazem {{GrassPrikaz|r.in.wms}} v kombinaci s přepínačem <tt>-l</tt>
<source lang="bash">
r.in.wms -l mapserver=http://www.bnhelp.cz/ows/crtopo
</source>
=== Postklasifikační úpravy ===
Viz {{153YZODCv|10|Postklasifikační úpravy|10.cvičení}}.
<source lang="bash">
r.reclass.area input=Brg_mlc output=tmp1 greater=1
r.grow.distance input=tmp1 value=tmp2
r.neighbors input=tmp2 output=Brg_mlc_post method=mode
r.colors map=Brg_mlc_post raster=tp
g.remove rast=tmp1,tmp2
</source>
{{YouTube|1ACAV3ZZO9k|desc=Příklad post-klasifikačních úprav v grafickém modeleru}}
[[Image:grass-postkl.png|thumb|640px|center|Výsledek postklasifikačních úprav (MLC)]]
[[Image:grass-postkl-smap.png|thumb|640px|center|Výsledek postklasifikačních úprav (SMAP)]]
== Skript na závěr ==
Tento skript pro vybraný mapset (scénu) provede výše uvedené analýzy.
<source lang=python>
#!/usr/bin/python
#%module
#% description: Davkovy vypocet pro ZODH.
#%end
#%option
#% key: mapset
#% description: Nazev mapsetu
#% gisprompt: old,mapset,mapset
#% key_desc: name
#% required: yes
##% answer: ceske-budejovice-2000
#%end
#%option
#% key: city
#% description: Nazev mesta
#% required: yes
#% options: Ceske Budejovice,Jablonec nad Nisou,Teplice,Vsetin,Znojmo
##% answer: Ceske Budejovice
#%end
#%option
#% key: mtl
#% description: Cesta k MTL souboru
#% gisprompt: old,file,file
#% key_desc: path
#% required: yes
##% answer: /work/geodata/zodh2010/data/ceske-budejovice-2000/L5191026_02620020627_MTL.txt
#%end
#%option
#% key: output_prefix
#% description: Prefix pro vystupni datove vrstvy
#% answer: B
#%end
#%flag
#% key: r
#% description: Pred vypoctem vycistit mapset
#%end
import os
import sys
import tempfile
import grass.script as grass
def create_atcorr_file(mtlfile):
    atcorrfile = list()
    text = ''
    bandid = None
    try:
        fd = open(mtlfile)
        for line in fd.readlines():
            if 'SENSOR_ID' in line:
                sensor = line.split('=')[1].strip().replace('"', '')
                if sensor != 'ETM+':
                    atcorrfile.append('7')
                    if sensor == 'MSS':
                        bandid = 31
                    else:
                        bandid = 25
                else:
                    atcorrfile.append('8')
                    bandid = 61
            elif 'ACQUISITION_DATE' in line:
                year, month, day = line.split('=')[1].strip().split('-')
                text = '%s %s ' % (month, day)
            elif 'SCENE_CENTER_SCAN_TIME' in line:
                time = line.split('=')[1].strip().replace('"', '')
                h, m, s = time.split(':')
                m = int(m) * 100 / 60
                text += '%d.%.2d ' % (int(h), m)
                region = grass.parse_command('g.region',
                                            flags = 'lg')
                text += '%f %f' % (float(region['center_long']),
                                  float(region['center_lat']))
                atcorrfile.append(text)
        atcorrfile.append('2')      # midlatitude summer
        atcorrfile.append('3')      # urban model
        atcorrfile.append('15')    # visibility
        stats = grass.parse_command('r.univar',
                                    flags = 'g',
                                    map = 'dem')
        atcorrfile.append('-%f' % (float(stats['mean']) / 1000))
        atcorrfile.append('-1000')
    finally:
        fd.close()
   
    return atcorrfile, bandid
def main():
    mapset  = options['mapset']
    mesto  = options['city']
    mtlfile = options['mtl']
    prefix  = options['output_prefix']
   
    if flags['r']:
        grass.info("Odstranuji vsechny rastrove a vektorove mapy...")
        grass.run_command('g.mremove',
                          flags = 'f',
                          rast = '*',
                          vect = '*')
   
    path = grass.mapsets()
    current_mapset = grass.gisenv()['MAPSET']
   
    # globalni promenne
    os.environ['GRASS_OVERWRITE'] = "1"
    os.environ['GRASS_VERBOSE']  = "1"
   
    # pridej mapset do cesty
    grass.run_command('g.mapsets',
                      mapset = '%s,PERMANENT,%s' % \
                          (current_mapset, mapset))
   
    # vytvor polygon mesta
    grass.info("Vytvarim polygon mesta...")
    grass.run_command('v.extract',
                      input = 'obce',
                      output = mesto.replace(' ', '_'),
                      where="nazev_eng = '%s'" % mesto)
    mesto = mesto.replace(' ', '_')
   
    # nastaveni regionu
    grass.info("Nastavuji vypocetni region...")
    grass.run_command('g.region',
                      vect = mesto,
                      rast = 'B1',
                      n = 'n+1e4',
                      s = 's-1e4',
                      w = 'w-1e4',
                      e = 'e+1e4')
   
    # vytvor kopii rastrovych dat v ramci vypocetniho regionu
    grass.info("Vytvarim kopie rastrovych dat...")
    for band in grass.read_command('g.mlist',
                                  type = 'rast',
                                  pattern = 'B*',
                                  mapset = mapset).splitlines():
        oname = '%srg%s' % (prefix, band[1:])
        grass.info(" %s -> %s" % (band, oname))
        grass.mapcalc(oname + ' = ' + band)
        grass.run_command('r.null',
                          map = oname,
                          setnull = 0)
        grass.run_command('r.colors',
                          map = oname,
                          color = 'grey.eq')
   
    # vypocet zareni
    grass.info("Konvertuji rastrova data DH -> hodnoty zareni...")
    grass.run_command('i.landsat.toar',
                      flags = 'tr',
                      input_prefix = '%srg' % prefix,
                      output_prefix = '%s_rad' % prefix,
                      metfile = mtlfile)
   
    # atmosfericke korekce
    grass.info("Aplikuji atmosfericke korekce...")
    atcorr, bandid = create_atcorr_file(mtlfile)
    isMss = bandid == 31
    i = 1
    for band in grass.read_command('g.mlist',
                                  type = 'rast',
                                  pattern = '%s_rad*' % prefix).splitlines():
        oband = band.split('_', 1)[0] + '_cor' + str(i)
        grass.info("Zpracovavam <%s> -> <%s>..." % (band, oband))
        atcorr.append('%d' % bandid)
        atcorrfile = tempfile.NamedTemporaryFile()
        atcorrfile.write('\n'.join(atcorr))
        atcorrfile.seek(0)
        grass.run_command('i.atcorr',
                          iimg = band,
                          icnd = atcorrfile.name,
                          oimg = oband)
        bandid += 1
        i += 1
        atcorr.pop()
   
    # hodnoty odrazivost
    grass.info("Konvertuji rastrova data DH -> hodnoty odrazivosti...")
    grass.run_command('i.landsat.toar',
                      flags = 't',
                      input_prefix = '%srg' % prefix,
                      output_prefix = '%s_ref' % prefix,
                      metfile = mtlfile)
   
    # detekce mraku
    grass.info("Analyzuji oblacnost...")
    if isMss:
        grass.info("...nelze pro MSS")
    else:
        grass.run_command('i.landsat.acca',
                          flags = '5',
                          input_prefix = '%s_ref' % prefix,
                          output = '%s_acca' % prefix)
   
    # obnov vyhledavaci cestu
    grass.run_command('g.mapsets',
                      mapset = ','.join(path))
   
    return 0
if __name__ == "__main__":
    options, flags = grass.parser()
    sys.exit(main())
</source>


== Externí odkazy ==
== Externí odkazy ==


* [http://geo.fsv.cvut.cz/~landa/vyuka/153ZODH/load%20USGS.pdf Poznámky z cvičení YZD1]
* [http://geo.fsv.cvut.cz/~landa/vyuka/153ZODH/load%20USGS.pdf Poznámky z cvičení YZD1]
* [http://grass.osgeo.org/wiki/LANDSAT GRASS User Wiki - some notes about LandSat]
* [http://grass.osgeo.org/wiki/MODIS#Removing_holes Removing holes]
* [http://toroid.org/ams/landsat-and-grass An introduction to using Landsat data with GRASS]
* [http://landsat.usgs.gov/best_spectral_bands_to_use.php Frequently Asked Questions about the Landsat Missions]
* [http://blog.neteler.org/processing-landsat8-data-in-grass-gis-7/ Processing Landsat 8 data in GRASS GIS 7: Import and visualization]
{{GRASS}}
{{GRASS}}

Aktuální verze z 9. 12. 2013, 15:25

Tato stránka obsahuje poznámky pro stažení dat LandSat Thematic Mapper a následné zpracování těchto dat v prostřední GRASS GIS. Více v rámci předmětu Zpracování obrazových dat.

Postup pro manuální stažení dat

Návod pro http://glovis.usgs.gov:

Import dat

Data je nejprve nutno dekomprimovat, např.

tar xvzf LT51910252009213MOR00.tar.gz

Příklad skriptu pro Bash, který rozbalí všechny dostupné archivy v aktuálním adresáři (pro každý rozbalený archiv vytvoří samostatný adresář).

#!/bin/sh

for file in *.tar.gz ; do
    echo "Zpracovavam $file..."
    dir=${file%.tar.gz}
    if [ -d $dir ] ; then
        rm -rf $dir
    fi
    mkdir $dir
    tar -xzf $file -C $dir
done

Archiv obsahuje několik souborů ve formátu geotiff (jeden soubor na kanál), seznam identických bodů (GCP, ground control points), metadata v textové podobě (MTL) a soubor README.GTF.

ls -w1
L5191025_02520090801_B10.TIF
L5191025_02520090801_B20.TIF
L5191025_02520090801_B30.TIF
L5191025_02520090801_B40.TIF
L5191025_02520090801_B50.TIF
L5191025_02520090801_B60.TIF
L5191025_02520090801_B70.TIF
L5191025_02520090801_GCP.txt
L5191025_02520090801_MTL.txt
README.GTF

Nástroj knihovny GDAL gdalinfo poslouží pro kontrolu metadat, např.

gdalinfo L5191025_02520090801_B10.TIF
Driver: GTiff/GeoTIFF
Files: L5191025_02520090801_B10.TIF
Size is 8441, 7441
Coordinate System is:
PROJCS["unnamed",
    GEOGCS["WGS 84",
        DATUM["WGS_1984",
            SPHEROID["WGS 84",6378137,298.257223563,
                AUTHORITY["EPSG","7030"]],
            AUTHORITY["EPSG","6326"]],
        PRIMEM["Greenwich",0],
        UNIT["degree",0.0174532925199433],
        AUTHORITY["EPSG","4326"]],
    PROJECTION["Transverse_Mercator"],
    PARAMETER["latitude_of_origin",0],
    PARAMETER["central_meridian",15],
    PARAMETER["scale_factor",0.9996],
    PARAMETER["false_easting",500000],
    PARAMETER["false_northing",0],
    UNIT["unknown",1],
    AUTHORITY["EPSG","32633"]]
Origin = (398699.999999999941792,5679000.000000000000000)
Pixel Size = (30.000000000000000,-30.000000000000000)
Metadata:
  AREA_OR_POINT=Point
Image Structure Metadata:
  INTERLEAVE=BAND
Corner Coordinates:
Upper Left  (  398700.000, 5679000.000) ( 13d32'54.25"E, 51d15'12.06"N)
Lower Left  (  398700.000, 5455770.000) ( 13d36'29.29"E, 49d14'46.73"N)
Upper Right (  651930.000, 5679000.000) ( 17d10'35.91"E, 51d14'31.60"N)
Lower Right (  651930.000, 5455770.000) ( 17d 5'13.62"E, 49d14'9.03"N)
Center      (  525315.000, 5567385.000) ( 15d21'18.49"E, 50d15'29.05"N)
Band 1 Block=8441x1 Type=Byte, ColorInterp=Gray

Vytvoření lokace

Hromadný import dat

Data lze naimportovat pomocí modulu r.in.gdal (File -> Import raster data -> Common import formats), ukázka z příkazové řadky (přepínač -e nastaví po importu region podle vstupní dat).

r.in.gdal -e input=L5191025_02520090801_B10.TIF output=b10 title="kanal 1"

Základní metadata vypíše r.info.

r.info map=b10
 +----------------------------------------------------------------------------+
 | Layer:    b10                            Date: Thu Sep 16 17:48:23 2010    |
 | Mapset:   PERMANENT                      Login of Creator: martin          |
 | Location: tm                                                               |
 | DataBase: /home/martin/grassdata                                           |
 | Title:    kanal 1 ( b10 )                                                  |
 | Timestamp: none                                                            |
 |----------------------------------------------------------------------------|
 |                                                                            |
 |   Type of Map:  raster               Number of Categories: 0               |
 |   Data Type:    CELL                                                       |
 |   Rows:         7441                                                       |
 |   Columns:      8441                                                       |
 |   Total Cells:  62809481                                                   |
 |        Projection: UTM (zone 33)                                           |
 |            N:    5679000    S:    5455770   Res:    30                     |
 |            E:     651930    W:     398700   Res:    30                     |
 |   Range of data:    min = 0  max = 255                                     |
 |                                                                            |
 |   Data Description:                                                        |
 |    generated by r.in.gdal                                                  |
 |                                                                            |
 |   Comments:                                                                |
 |    r.in.gdal -e input="L5191025_02520090801_B10.TIF" output="b10"          |
 |                                                                            |
 +----------------------------------------------------------------------------+

Data lze hromadně naimportovat pomocí jednoduchého skriptu. Následuje ukázka pro Bash a Python.

Bash

Ukázka skriptu pro Bash (pro každou družicovou scénu vytvoří vlastní mapset).

#!/bin/sh
 
function import()
{
    idx=1
    for file in `ls *.TIF 2>/dev/null`; do
        oname=`echo $file | cut -d'_' -f3 | cut -d'.' -f1`
	oname=${oname:0:2}
        g.message "Importuji $file -> $oname@$mapset..."
        g.mapset -c mapset=$mapset --quiet 2>/dev/null
        r.in.gdal -e input=$file output=$oname title="kanal $idx" --overwrite --quiet
        idx=$(($idx+1))
    done
}
 
eval `g.gisenv`
 
if test -n "$1" ; then
    mapset=$1
    cd $1
    import
else
    for dir in `find . -maxdepth 1 -mindepth 1 -type d`; do
	mapset=`echo $dir | cut -d'/' -f2`
	cd $dir
	import
	cd ..
    done
fi
 
g.mapset mapset=$MAPSET --quiet

Příklad spuštění:

  • ./import.sh projde všechny adresáře
  • ./import.sh LM41890261983200FFF03 naimportuje data pouze z vybraného adresáře

Python

Ukázka skriptu pro Python (pro každou družicovou scénu vytvoří vlastní mapset). Tento skript navíc nastaví timestamp rastrových dat.

#!/usr/bin/python
 
import os
import sys
import glob
import grass.script as grass
 
def get_timestamp(mapset):
    try:
        metafile = glob.glob(mapset + '/*MTL.txt')[0]
    except IndexError:
        return
 
    result = dict()
    try:
        fd = open(metafile)
        for line in fd.readlines():
            line = line.rstrip('\n')
            if len(line) == 0:
                continue
            if 'ACQUISITION_DATE' in line:
                result['date'] = line.strip().split('=')[1].strip()
    finally:
        fd.close()
 
    return result
 
def import_tifs(mapset):
    meta = get_timestamp(mapset)
    for file in os.listdir(mapset):
        if os.path.splitext(file)[-1] != '.TIF':
            continue
        ffile = os.path.join(mapset, file)
        name = os.path.splitext(file)[0].split('_')[-1] #B10, B61
        if name[-1] == '0':
            name = name[:2]
        kanal = int(name[1]) # B
        grass.message('Importuji %s -> %s@%s...' % (file, name, mapset))
        grass.run_command('g.mapset',
                          flags = 'c',
                          mapset = mapset,
                          quiet = True,
                          stderr = open(os.devnull, 'w'))
        grass.run_command('r.in.gdal',
                          input = ffile,
                          output = name,
                          quiet = True,
                          overwrite = True,
                          title = 'kanal %d' % kanal)
        if meta:
            year, month, day = meta['date'].split('-')
            if month == '01':
                month = 'jan'
            elif month == '02':
                month = 'feb'
            elif month == '03':
                month = 'mar'
            elif month == '04':
                month = 'apr'
            elif month == '05':
                month = 'may'
            elif month == '06':
                month = 'jun'
            elif month == '07':
                month = 'jul'
            elif month == '08':
                month = 'aug'
            elif month == '09':
                month = 'sep'
            elif month == '10':
                month = 'oct'
            elif month == '11':
                month = 'nov'
            elif month == '12':
                month = 'dec'
 
            grass.run_command('r.timestamp',
                              map = name,
                              date = ' '.join((day, month, year)))
 
def main():
    if len(sys.argv) == 1:
        for directory in filter(os.path.isdir, os.listdir(os.getcwd())):
            import_tifs(directory)
    else:
        import_tifs(sys.argv[1])
 
if __name__ == "__main__":
    main()

wxGUI

Dále lze data naimportovat pomocí wxGUI.

File -> Import raster data -> Common formats import

Vytvoření přehledky

Následující skript v jazyce Python vytvoří vektorovou přehledku včetně atributové tabulky s názvy mapsetů.

#!/usr/bin/python

import os
import sys
import grass.script as grass

maps = [] # tmp maps
cats = {} # attribute table
cat = 1

os.environ['GRASS_VERBOSE'] = '0'
os.environ['GRASS_OVERWRITE'] = '1'

# list mapsets
for mapset in grass.mapsets():
    if mapset[:2] not in ('LE', 'LM', 'LT'):
        continue
    grass.run_command('g.region',
                      rast = "B1@%s" % mapset)
    grass.run_command('v.in.region',
                      output = mapset,
                      type = 'area',
                      cat = cat)
    cats[cat] = mapset
    maps.append(mapset)
    
    cat += 1

# patch maps
grass.run_command('v.patch',
                  input = ','.join(maps),
                  output = 'prehledka')

# create attribute table
grass.run_command('v.db.addtable',
                  map = 'prehledka',
                  columns = 'cat integer, mapset varchar(255)')
for cat, mapset in cats.iteritems():
    grass.run_command('v.db.update',
                      map = 'prehledka',
                      column = 'mapset',
                      value = mapset,
                      where = "cat = %d" % cat)

for m in maps:
    grass.run_command('g.remove',
                      quiet = True,
                      vect = m)
Vektorová přehledka mapsetů s importovanými daty Landsat

Test lokace

Následující skript v jazyce Python vypíše pro danou lokaci (město) relevantní mapsety včetně roku, ve kterém byly snímky Landsat pořízeny.

#!/usr/bin/python

#%Module
#% description: Vypis mapsety pro danou lokaci
#%End
#%Option
#% key: mesto
#% description: Nazev mesta (bez diakritiky)
#% type: string
#% required: yes
#%End

import os
import sys
import atexit

import grass.script as grass

tmp_maps = []
def cleanup():
    grass.run_command('g.remove',
                      vect = '%s' % ','.join(tmp_maps))
    
def main():
    os.environ['GRASS_VERBOSE'] = '0'
    os.environ['GRASS_OVERWRITE'] = '1'
    
    grass.run_command('v.extract',
                      input = 'obce',
                      output = 'obce_tmp',
                      where = "nazev_eng = '%s'" % options['mesto'])
    tmp_maps.append('obce_tmp')
    
    mapsets = []
    
    # list mapsets
    for mapset in grass.mapsets():
        if mapset[:2] not in ('LE', 'LM', 'LT'):
            continue
        grass.run_command('g.region',
                          rast = "B1@%s" % mapset)
        grass.run_command('v.in.region',
                          output = mapset,
                          type = 'area')
        tmp_maps.append(mapset)
        
        grass.run_command('v.select',
                          flags = 't',
                          ainput = mapset,
                          atype = 'area',
                          binput = 'obce_tmp',
                          btype = 'area',
                          output = 'mapset_tmp')
        
        if bool(grass.vector_info_topo('mapset_tmp')['areas']):
            ret = grass.read_command('r.timestamp',
                                     map = 'B1@%s' % mapset)
            year = ''
            if ret:
                year = ret.split(' ')[-1].strip()
            
            print mapset, year
    
        grass.run_command('g.remove',
                          vect = 'mapset_tmp')
    
    return 0

if __name__ == "__main__":
    atexit.register(cleanup)
    options, flags = grass.parser()
    sys.exit(main())

Příklad:

test.py mesto='Ceske Budejovice'
LE71900262010185ASN00 2010
LM41910261984105FFF03 1984
LT41910261992191XXX02 1992
LT51900261991173XXX02 1991
LT51900262002171MTI00 2002
LT51900262010193MOR00 2010
LT51910262002178MTI00 2002
LT51910262010184MOR00 2010
LT51920262002137MTI00 2002

Předzpracování dat (no-data)

Po importu dat je vhodné nahradit hodnotu '0' za 'null' (žádná data, no-data) - r.null.

g.copy rast=B1@ceske-budejovice-2000,B1
g.region rast=B1
r.null map=B1 setnull=0

Tuto operaci lze provést hromadně pro všechny kanály družicové scény z vybraného mapsetu (více k tématu skriptování v jazyce Python na portálu FreeGIS).

#!/usr/bin/python

from grass.pygrass.gis import Mapset
from grass.pygrass.modules.shortcuts import general as g
from grass.pygrass.modules.shortcuts import raster  as r

mapset = 'ceske-budejovice-2000'
    
for band in sorted(Mapset(mapset).glist('rast')):
    g.message("Kopiruji %s..." % band)
    g.copy(rast = '%s@%s,%s' % (band,mapset,band))
    g.region(rast = band)
    g.message("Nastavuji no-data pro %s..." % band)
    r.null(map = band, setnull='0')

Další možností je vytvořit model pomocí grafického modeleru File → Graphical Modeler

Příklad modelu - pro každý kanál

  1. vytvoří kopii kanálu v aktuálním mapsetu (nutné pro r.null)
  2. nastaví region
  3. nahradí hodnotu 0 hodnotou NULL
{{#ev:youtube|LFdn1VozMfo|550|center|Model pro předzpracování družicových dat (hodnoty NULL)}}
Porovnání barevné syntézy ve skutečných barvách s hodnotami 0 a NULL

Nastavení vyhledávací cesty

Mapset s naimportovanými daty přidáme do vyhledávací cesty pomocí g.mapsets

g.mapsets mapset=LM41890261983200FFF03 operation=add

nebo pomocí wxGUI

Settings -> GRASS working environment -> Mapset access
Nastavení přístupu k mapsetům

Vizualizace dat

Příklad vizualizace kompozitního snímku (kanály 453)

Zájmové území

Nejprve vytvoříme vektorovou vrstvu s polygonem zájmového města (v tomto případě 'Pardubice' - název města zadejte bez diakritiky).

v.extract input=obce output=par where="nazev_eng = 'Pardubice'

Výpočetní region nastavíme pomocí g.region, např. pro město "Pardubice" (rastrovou vrstvu zadáváme pro nastavení prostorového rozlišení) s offsetem 10km.

g.region rast=B1 vect=par n=n+1e4 s=s-1e4 w=w-1e4 e=e+1e4 save=par
Nastavení výpočetního regionu na základě vektorové vrstvy s daným offsetem

Opětovné nastavení výpočetního regionu

g.region region=par


Interpolace chybějících dat

Pro interpolaci chybějících hodnot lze použít modul r.fillnulls.

g.region rast=B1
r.fillnulls input=B1 output=B1_fill

Atmosferické korekce

Další poznámky najdete na GRASS Wiki.

Provedeme konverzi digitálních hodnot (DH) na hodnoty záření na vrcholu atmosféry (TOA) pomocí modulu i.landsat.toar (soubory s metadaty jsou dostupné na adrese: http://geo102.fsv.cvut.cz/zodh/metadata/).

i.landsat.toar -r input_prefix=Brg output_prefix=B_rad \
 metfile=/work/geodata/zodh2010/data/jablonec-nad-nisou-1980/L4191025_02519840414_MTL.txt

Připravíme konfigurační soubor pro i.atcorr. Tento modul aplikuje atmosferické korekce, přepočte hodnoty záření na vrcholu atmosféry na hodnoty absolutní odrazivosti objektů.

Některé údaje získáme z metadatového souboru (MTL), jiné spočteme.

  • Dat a čas pořízení dat
cd /work/geodata/zodh2010/data/jablonec-nad-nisou-1980/
grep -a 'ACQUISITION_DATE' L4191025_02519840414_MTL.txt
    ACQUISITION_DATE = 1984-04-14
grep -a 'SCENE_CENTER_SCAN_TIME' L4191025_02519840414_MTL.txt
    SCENE_CENTER_SCAN_TIME = 09:17:32.4240000Z
  • Střed scény v zeměpisných souřadnicích
 g.region -lg
...
center_long=15.16068438
center_lat=50.72648033
...

nebo

 g.region -c
center easting:  511341.280354
center northing: 5619440.568602
echo '511341|5619440' | m.proj -ogd --q
15.16068045|50.72664675

Poznámka: V prvním případě se používá elipsoid lokace, v druhém WGS84.

  • Průměrná výška
r.univar map=dem --q | grep 'mean:'
mean: 552.711

Příklad konfiguračního souboru:

7                                    # geometricke podminky Landsat TM
04 14 9.28 15.16068045 50.72664675   # mesic den h.mmm (GMT) zem. sirka delka
2                                    # atmosfericky model midlatitude summer
3                                    # aerosol model urban
50                                   # viditelnost [km]
-0.552                               # prumerna vyska [km]
-1000                                # sensor na urovni nosice
31                                   # 1. kanal Landsat 5 TM

Atmosferické korekce nakonec aplikujeme příkazem

i.atcorr iimg=B_rad1 icnd=atcorr.txt oimg=B_cor1

Detekce oblačnosti

Detekovat mraky (a jejich teplotu) umožňuje modul i.landsat.acca.

Nejprve konvertuje DH na hodnoty odrazivosti (soubory s metadaty (MTL) jsou dostupné zde).

i.landsat.toar input_prefix=Brg output_prefix=B_ref metfile=L4191025_02519840414_MTL.txt

Provedeme výpočet (pouze pro TM/ETM+)

i.landsat.acca -5 input_prefix=B_ref output=B_acca
r.report map=B_acca units=p
                                              
+-----------------------------------------------------------------------------+
|                         RASTER MAP CATEGORY REPORT                          |
|LOCATION: zod2010                                    Mon Nov  8 23:05:41 2010|
|-----------------------------------------------------------------------------|
|          north: 5633223.50868776    east: 525403.19584718                   |
|REGION    south: 5605657.62851621    west: 497279.36486102                   |
|          res:        29.99551705    res:      30.01476092                   |
|-----------------------------------------------------------------------------|
|MASK:none                                                                    |
|-----------------------------------------------------------------------------|
|MAP: LANDSAT-5 TM Automatic Cloud Cover Assessment (B_acca@landa in landa)   |
|-----------------------------------------------------------------------------|
|                        Category Information                          |   %  |
|#|description                                                         | cover|
|-----------------------------------------------------------------------------|
|6|Cold cloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . .|  0.01|
|9|Warm cloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . .|  0.10|
|*|no data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .| 99.89|
|-----------------------------------------------------------------------------|
|TOTAL                                                                 |100.00|
+-----------------------------------------------------------------------------+

Nastavení masky

Příklad nastavení masky oblačnosti pomocí r.mask

r.mask -i input=B_acca maskcats="6 9"
Barevná syntéza 321
Výsledek detekce oblačnosti
Barevná syntéza 321 s maskou oblačnosti

Výpočet pozice slunce

Pozici slunce můžeme určit pomocí r.sunmask. Nejprve z metadat určíme vstupní parametry.

cd /work/geodata/zodh2010/data/jablonec-nad-nisou-1980/
grep -a 'ACQUISITION_DATE' L4191025_02519840414_MTL.txt
    ACQUISITION_DATE = 1984-04-14
grep -a 'SCENE_CENTER_SCAN_TIME' L4191025_02519840414_MTL.txt
    SCENE_CENTER_SCAN_TIME = 09:17:32.4240000Z
r.sunmask -sg elevation=dem year=1984 month=4 day=14 hour=9 minute=17 second=32 timezone=0
sunazimuth=143.930862
sunangleabovehorizon=44.188877

Vypočtena data můžeme porovnat s hodnotami uvedenými v metadatovém souboru.

grep -a 'SUN_' L4191025_02519840414_MTL.txt
    SUN_AZIMUTH = 143.8553375
    SUN_ELEVATION = 44.1660847

Klasifikace dat

Neřízená klasifikace

Více na stránkách 10. cvičení předmětu Zpracování obrazových dat.

Řízená klasifikace

Více na stránkách 11. cvičení předmětu Zpracování obrazových dat.

Pokud máme k dispozici výsledky analýzy oblačnosti, muže je použít pro vytvoření masky

r.mask -i input=B_acca maskcats="6 9"

Nejprve vytvoříme obrazovou skupinu (předpokládejme, že Brg jsou obrazové kanály po atmosferické korekci)

i.group group=Brg subgroup=Brg input=Brg1,Brg2,Brg3,Brg4,Brg5,Brg7

nebo pomocí dialogu wxGUI Imagery → Develop images and groups → Create/edit group

Založení obrazové skupiny ve wxGUI (výběr rastrových map)
Založení obrazové skupiny ve wxGUI

Barevnou kompozici lze vytvořit pomocí r.composite

g.region rast=Brg4
r.composite red=Brg4 green=Brg5 blue=Brg3 output=Brg453

Provedeme digitalizaci vybraných trénovacích ploch, viz cvičení předmětu Zpracování obrazových dat. Trénovací plochy definujeme pro následující třídy:

  • Zástavba
  • Nelesní zeleň
  • Lesní porosty
  • Zemědělská půda
  • Vodní plochy

Digitalizovat trénovací plochy lze ve specializovaném nástroji g.gui.iclass (více zde). Anebo nepřímo pomocí digitalizačního nástroje - viz návod níže.

{{#ev:youtube|v7f07jzt5uo|550|center|Vytvoření vektorové vrstvy trénovacích ploch, digitalizace ploch lesních porostů, vodních ploch a zástavby}}
Atributová tabulka vektorové vrstvy trénovacích ploch
Atributy vektorové vrstvy trénovacích ploch

Po přidání atributového sloupce GRASSRGB (v.db.addcolumn) můžeme pro vektorovou vrstvu trénovacích ploch nastavit tabulku barev.

v.db.addcol map=tp column="GRASSRGB varchar(11)"
Vector -> Manage colors -> Color rules
Nastavení tabulky barev vektorové vrstvy trénovacích ploch

Pro aktivaci tabulky barev použijeme přepínač -a modulu d.vect

 d.vect -a map=tp

Dále provedeme rasterizaci trénovacích ploch

 v.to.rast input=tp type=area output=tp use=attr column=cat rgbcolumn=GRASSRGB labelcolumn=trida
Rasterizované trénovací plochy, na pozadí barevná kompozice RGB 453

A na závěr klasifikaci dat

i.gensig trainingmap=tp group=Brg subgroup=Brg signaturefile=sig
i.maxlik group=Brg subgroup=Brg sigfile=sig class=Brg_mlc reject=Brg_mlc_ref
i.gensigset trainingmap=tp group=Brg subgroup=Brg signaturefile=sig_smap
i.smap group=Brg subgroup=Brg signaturefile=sig_smap output=Brg_smap

Můžeme převzít tabulku barev pomocí příkazu

r.colors map=Brg_mlc raster=tp
r.colors map=Brg_smap raster=tp
Výsledek klasifikace (MLC)
Výsledek klasifikace (SMAP)

Využití dat z projektu OpenStreetMap

Pro určení trénovacích ploch lze také využít tématické vrstvy z mapsetu osm vytvořeného z dat OpenStreetMap. Příklad vytvoření pomocných tématických vrstev (viz wiki OSM):

v.extract input=landuse output=landuse_zastavba where="type = 'brownfield' or type = 'cemetery' or type = 'commercial' \
  or type = 'construction' or type = 'farmyard' or type = 'garages' or type = 'industrial' or type = 'residential' \
  or type = 'retail'" new=1
v.extract input=landuse output=landuse_zelen where="type = 'allotments' or type = 'grass' or type = 'greenfield' or \
  type = 'greenhouse_horticulture' or type = 'recreation_ground' or type='village_green' or type = 'park'" new=2
v.extract input=natural output=landuse_les where="type = 'forest'" new=3
v.extract input=landuse output=landuse_zemedel where="type = 'farm' or type = 'farmland' or type = 'orchard' \
  or type = 'vineyard'" new=4
v.extract input=landuse output=landuse_voda where="type = 'reservoir'" new=5

Alternativně můžeme vytvořit "reklasifikovanou" landuse rastrovou mapu:

v.proj location=osm_shp mapset=PERMANENT input=landuse_zastavba
v.proj location=osm_shp mapset=PERMANENT input=landuse_zelen
v.proj location=osm_shp mapset=PERMANENT input=landuse_les
v.proj location=osm_shp mapset=PERMANENT input=landuse_zemedel
v.proj location=osm_shp mapset=PERMANENT input=landuse_voda

g.region rast=B1@beroun-2000 vect=cr
v.to.rast input=landuse_zastavba output=landuse_zastavba type=area use=val value=1
v.to.rast input=landuse_zelen output=landuse_zelen type=area use=val value=2
v.to.rast input=landuse_les output=landuse_les type=area use=val value=3
v.to.rast input=landuse_zemedel output=landuse_zemedel type=area use=val value=4
v.to.rast input=landuse_voda output=landuse_voda type=area use=val value=5

r.patch input=landuse_zastavba,landuse_zelen,landuse_les,landuse_zemedel,landuse_voda out=landuse_tridy

r.colors map=landuse_tridy rules=- << EOF
1 139:105:20
2 59:227:59
3 7:121:7
4 191:191:191
5 0:0:255
nv 255:255:255
default 255:255:255
EOF


Landuse dle OSM (zástavba - hnědá, zemědělská půda - šedivá, lesní porosty - tmavě zelená, zeleň - zelená, vodní plochy - modrá)
Landuse (zástavba - hnědá, zemědělská půda - šedivá, lesní porosty - tmavě zelená, zeleň - zelená, vodní plochy - modrá), hranice města (červená), na pozadí barevná kompozice 453

Využití dat z veřejně dostupných WMS

Jako podkladovou vrstvu můžeme také použít data z WMS serveru

http://www.bnhelp.cz/ows/crtopo

Seznam dostupných vrstev získáme příkazem r.in.wms v kombinaci s přepínačem -l

r.in.wms -l mapserver=http://www.bnhelp.cz/ows/crtopo

Postklasifikační úpravy

Viz 10.cvičení.

r.reclass.area input=Brg_mlc output=tmp1 greater=1
r.grow.distance input=tmp1 value=tmp2
r.neighbors input=tmp2 output=Brg_mlc_post method=mode
r.colors map=Brg_mlc_post raster=tp
g.remove rast=tmp1,tmp2
{{#ev:youtube|1ACAV3ZZO9k|550|center|Příklad post-klasifikačních úprav v grafickém modeleru}}
Výsledek postklasifikačních úprav (MLC)
Výsledek postklasifikačních úprav (SMAP)

Skript na závěr

Tento skript pro vybraný mapset (scénu) provede výše uvedené analýzy.

#!/usr/bin/python

#%module
#% description: Davkovy vypocet pro ZODH.
#%end
#%option
#% key: mapset
#% description: Nazev mapsetu
#% gisprompt: old,mapset,mapset
#% key_desc: name
#% required: yes
##% answer: ceske-budejovice-2000
#%end
#%option
#% key: city
#% description: Nazev mesta
#% required: yes
#% options: Ceske Budejovice,Jablonec nad Nisou,Teplice,Vsetin,Znojmo
##% answer: Ceske Budejovice
#%end
#%option
#% key: mtl
#% description: Cesta k MTL souboru
#% gisprompt: old,file,file
#% key_desc: path
#% required: yes
##% answer: /work/geodata/zodh2010/data/ceske-budejovice-2000/L5191026_02620020627_MTL.txt
#%end
#%option
#% key: output_prefix
#% description: Prefix pro vystupni datove vrstvy
#% answer: B
#%end
#%flag
#% key: r
#% description: Pred vypoctem vycistit mapset
#%end

import os
import sys
import tempfile
 
import grass.script as grass
 
def create_atcorr_file(mtlfile):
    atcorrfile = list()
    text = ''
    bandid = None
    try:
        fd = open(mtlfile)
        for line in fd.readlines():
            if 'SENSOR_ID' in line:
                sensor = line.split('=')[1].strip().replace('"', '')
                if sensor != 'ETM+':
                    atcorrfile.append('7')
                    if sensor == 'MSS':
                        bandid = 31
                    else:
                        bandid = 25
                else:
                    atcorrfile.append('8')
                    bandid = 61
            elif 'ACQUISITION_DATE' in line:
                year, month, day = line.split('=')[1].strip().split('-')
                text = '%s %s ' % (month, day)
            elif 'SCENE_CENTER_SCAN_TIME' in line:
                time = line.split('=')[1].strip().replace('"', '')
                h, m, s = time.split(':')
                m = int(m) * 100 / 60
                text += '%d.%.2d ' % (int(h), m)
                region = grass.parse_command('g.region',
                                             flags = 'lg')
                text += '%f %f' % (float(region['center_long']),
                                   float(region['center_lat']))
                atcorrfile.append(text)
 
        atcorrfile.append('2')      # midlatitude summer
        atcorrfile.append('3')      # urban model
        atcorrfile.append('15')     # visibility
        stats = grass.parse_command('r.univar',
                                    flags = 'g',
                                    map = 'dem')
        atcorrfile.append('-%f' % (float(stats['mean']) / 1000))
        atcorrfile.append('-1000')
    finally:
        fd.close()
    
    return atcorrfile, bandid
 
def main():
    mapset  = options['mapset']
    mesto   = options['city']
    mtlfile = options['mtl']
    prefix  = options['output_prefix']
    
    if flags['r']:
        grass.info("Odstranuji vsechny rastrove a vektorove mapy...")
        grass.run_command('g.mremove',
                          flags = 'f',
                          rast = '*',
                          vect = '*')
    
    path = grass.mapsets()
    current_mapset = grass.gisenv()['MAPSET']
    
    # globalni promenne
    os.environ['GRASS_OVERWRITE'] = "1"
    os.environ['GRASS_VERBOSE']   = "1"
    
    # pridej mapset do cesty
    grass.run_command('g.mapsets',
                      mapset = '%s,PERMANENT,%s' % \
                          (current_mapset, mapset))
    
    # vytvor polygon mesta
    grass.info("Vytvarim polygon mesta...")
    grass.run_command('v.extract',
                      input = 'obce',
                      output = mesto.replace(' ', '_'),
                      where="nazev_eng = '%s'" % mesto)
    mesto = mesto.replace(' ', '_')
    
    # nastaveni regionu
    grass.info("Nastavuji vypocetni region...")
    grass.run_command('g.region',
                      vect = mesto,
                      rast = 'B1',
                      n = 'n+1e4',
                      s = 's-1e4',
                      w = 'w-1e4',
                      e = 'e+1e4')
    
    # vytvor kopii rastrovych dat v ramci vypocetniho regionu
    grass.info("Vytvarim kopie rastrovych dat...")
    for band in grass.read_command('g.mlist',
                                   type = 'rast',
                                   pattern = 'B*',
                                   mapset = mapset).splitlines():
        oname = '%srg%s' % (prefix, band[1:])
        grass.info(" %s -> %s" % (band, oname))
        grass.mapcalc(oname + ' = ' + band)
        grass.run_command('r.null',
                          map = oname,
                          setnull = 0)
        grass.run_command('r.colors',
                          map = oname,
                          color = 'grey.eq')
    
    # vypocet zareni
    grass.info("Konvertuji rastrova data DH -> hodnoty zareni...")
    grass.run_command('i.landsat.toar',
                      flags = 'tr',
                      input_prefix = '%srg' % prefix,
                      output_prefix = '%s_rad' % prefix,
                      metfile = mtlfile)
    
    # atmosfericke korekce
    grass.info("Aplikuji atmosfericke korekce...")
    atcorr, bandid = create_atcorr_file(mtlfile)
    isMss = bandid == 31
    i = 1
    for band in grass.read_command('g.mlist',
                                   type = 'rast',
                                   pattern = '%s_rad*' % prefix).splitlines():
        oband = band.split('_', 1)[0] + '_cor' + str(i)
        grass.info("Zpracovavam <%s> -> <%s>..." % (band, oband))
        atcorr.append('%d' % bandid)
        atcorrfile = tempfile.NamedTemporaryFile()
        atcorrfile.write('\n'.join(atcorr))
        atcorrfile.seek(0)
        grass.run_command('i.atcorr',
                          iimg = band,
                          icnd = atcorrfile.name,
                          oimg = oband)
        bandid += 1 
        i += 1
        atcorr.pop()
    
    # hodnoty odrazivost
    grass.info("Konvertuji rastrova data DH -> hodnoty odrazivosti...")
    grass.run_command('i.landsat.toar',
                      flags = 't',
                      input_prefix = '%srg' % prefix,
                      output_prefix = '%s_ref' % prefix,
                      metfile = mtlfile)
    
    # detekce mraku
    grass.info("Analyzuji oblacnost...")
    if isMss:
        grass.info("...nelze pro MSS")
    else:
        grass.run_command('i.landsat.acca',
                          flags = '5',
                          input_prefix = '%s_ref' % prefix,
                          output = '%s_acca' % prefix)
    
    # obnov vyhledavaci cestu
    grass.run_command('g.mapsets',
                      mapset = ','.join(path))
    
    return 0
 
if __name__ == "__main__":
    options, flags = grass.parser()
    sys.exit(main())

Externí odkazy