Skip to content

Instantly share code, notes, and snippets.

@02JanDal
Last active November 13, 2019 12:25
Show Gist options
  • Save 02JanDal/1d7de055f7645bef5ca757467afbb85e to your computer and use it in GitHub Desktop.
Save 02JanDal/1d7de055f7645bef5ca757467afbb85e to your computer and use it in GitHub Desktop.
Python för Geodata
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Avsnitt 2.1: Hitta paket"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.1.1: Gå till den [officiella dokumentationen](https://docs.python.org/3/library/index.html) och leta reda på ett paket för att göra en enkel nedladdning (av t.ex. en webbsida)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# importera paketet här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.1.2: Använd Google för att hitta ett paket för att arbeta mot en PostgreSQL-databas"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# lägg in en länk till paketets officiella dokumentation som en kommentar här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Avsnitt 2.2: Transformationer med fiona och shapely"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.2.1: Installera `fiona` och `shapely`\n",
"\n",
"Varken `fiona` eller `shapely` finns installerade än. Du kan installera `fiona` genom att köra `%pip install fiona` i en cell, och motsvarande för `shapely`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# installera fiona och shapely"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.2.2: Öppna `idrottsplatser.geojson` med `fiona` och inspektera resultatet\n",
"\n",
"1. Spara resultatet av `fiona.open('idrottsplatser.geojson', 'r')` i en variabel, t.ex. `reader`\n",
"2. Vad innehåller `reader.meta` för information?\n",
" * **Tips:** Inledd med ett `from pprint import pprint` och använd sedan funktionen `pprint` för att skriva ut resultatet av `reader.meta`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.2.3: Idrottsplatserna i `fiona`\n",
"\n",
"Din `reader` kan användas som om den vore en lista! Undersök hur många idrottsplatser den innehåller och undersök hur första elementet ser ut (kom ihåg `pprint`!)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Varje rad i `fiona` är en mappning (`dict` eller i vissa fall `OrderedDict`), du kan alltså komma åt respektive nyckels innehåll genom att använda `minmappning['minnyckel']`. I många fall ligger det mappningar i mappning o.s.v., i vilket fall du kan ta dig djupare genom att skriva `minmappning['minförstanyckel']['minandranyckel']`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.2.4: Från `fiona` till `shapely` och tillbaka\n",
"\n",
"`fiona` innehåller tyvärr inga funktioner för buffring m.m., istället brukar man använda `shapely`. `shapely` vill dock ha geometrierna på ett annat sätt än hur `fiona` tillhandahåller dem, vi behöver därför kunna transformera mellan dessa två bibliotek.\n",
"\n",
"1. Börja med att från `shapely.geometry` importera `shape` och `mapping`\n",
"2. `shape` används för att konvertera till `shapely`. Prova anropa `shape` med `minrad['geometry']` (byt ut `minrad` mot passande) och skriv ut resultatet\n",
"3. `mapping` används för att konvertera tillbaka till `fiona`. Prova anropa `mapping` med resultatet du får från `shape`\n",
"4. Jämför resultatet från `mapping` med den ursprungliga `minrad['geometry']`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.2.5: Använd [`shapely`](https://shapely.readthedocs.io/en/latest/) för att skapa en ny, buffrad fil\n",
"\n",
"1. Läsa in filen `idrottsplatser.geojson`\n",
"2. Buffra alla _tennisplatser_ med 10 meter\n",
"3. Skriv de buffrade tennisplatserna till en ny GeoJSON\n",
"\n",
"Du har fått en början att utgå ifrån nedan:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from shapely.geometry import shape, mapping\n",
"import fiona\n",
"\n",
"with fiona.open('idrottsplatser.geojson', 'r') as reader:\n",
" # genom att använda `**reader.meta` får den nya filen exakt samma struktur (men den är ännu tom)\n",
" with fiona.open('idrottsplatser_buffrade.geojson', 'w', **reader.meta) as writer:\n",
" # tennisplatser har ANDAMAL 7\n",
" # du kan skapa ett shapely objekt från en fiona-geometri med `shape(rad['geometry'])`\n",
" # leta själv upp hur du buffrar med shapely i dess dokumentation\n",
" # du kan konvertera tillbaka till fiona-geometry med `mapping(...din shapely-geometri...)`\n",
" # och sist kan du skriva ett fiona objekt (som du fått av `reader`) med `writer.write(...)`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tips: Tog körningen tid? Importera `from tqdm.autonotebook import tqdm` och byt sedan ut din for-loop mot `for ... in tqdm(reader):`. Vad händer?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Avsnitt 2.3: Plotta tabeller med GeoPandas"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.3.1: Använd [`GeoPandas`](http://geopandas.org/) för att läsa in filen `idrottsplatser.geojson` och undersök resultatet\n",
"\n",
"* Använd metoden `head`\n",
"* Använd metoden `plot`, inklusive dess argument `column` (exempel här: http://geopandas.org/mapping.html#choropleth-maps)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tips: GeoPandas och det underliggande Pandas är otroligt kraftfulla, men tar lite tid att vänja sig vid. Några bra att veta saker i början:\n",
"* En tabell är en `DataFrame`, den består av en radindex (oftast \"radnummer\"), ett kolumnindex (\"kolumntitlar\") och sedan en massa data som du kan få ut som `Series` (som är som en 1-dimensionel `DataFrame`)\n",
"* För att få ut en kolumn använder du `df['kolumnnamn']`, en rad får du med `df.loc['radindex']` eller `df.iloc[radnummer]` (notera skillnaden mellan `loc` och `iloc`, `loc` går på de givna radindexen vilka kan vara siffror, strängar eller annat, `iloc` går på ordningen raderna står i)\n",
"* `df['kolumnnamn']` ger dig en `Series` där varje värde har index efter dess radindex i det `DataFrame` det kommer från, du kan alltså få ett specifikt värde med `df['kolumnnamn'][2]`. Omvänt så ger dig `loc` och `iloc` en `Series` där varje värde har index efter kolumnnamn, du kan alltså få ett specifikt värde med `df.loc[2]['kolumnnamn']`\n",
"* Ingen av Pandas funktioner modifierar det objekt du anropar det på, utan de skapar nya. T.ex. gör `df.head()` inte så att `df` blir 5 rader långt, utan ger dig en ny `DataFrame`. För att \"ändra\" det aktuella objektet kan du tilldela det nya värdet, t.ex. `df = df.head()`\n",
"* Läs mer här: https://pandas.pydata.org/pandas-docs/stable/getting_started/dsintro.html"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.3.2: Mer plottar\n",
"\n",
"Var det svårt att se resultatet av `plot`? GeoPandas/Pandas ger dig några olika \"indexerare\", bl.a. `cx` som gör det möjligt att välja objekt baserade på dess koordinater. Du kan använda `cx` såhär för att få en ny GeoDataFrame över endast Gävle:\n",
"\n",
"```python\n",
"gavle_df = df.cx[610000:625000, 6720000:6735000]\n",
"```\n",
"\n",
"Där siffrorna såklart är koordinater. Prova att göra en ny plot som endast innehåller idrottsplatser över Gävleområdet!\n",
"\n",
"Prova sedan att lägga till argumenten `legend=True` och `categorical=True` till `plot`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.3.3: Arbeta med Pandas\n",
"\n",
"Lägg märke till att teckenförklaringen i förgående plot bara innehöll siffror. Inte speciellt användbart! Vi kan ändra på det genom de funktioner som Pandas (som GeoPandas baserar på) erbjuder.\n",
"\n",
"* Byt ut alla `{}` så att koden blir korrekt\n",
"* Skapa en ny plot med legend"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"{} = gavle_df.replace(dict({}={\n",
" 1: 'bollplan', 2: 'fotbollsplan', 3: 'galoppbana', 4: 'bandyplan', 5: 'löparbana', 6: 'motorsportbana', 7: 'tennisbana', 8: 'travbana'}))\n",
"# din kod för att skapa plot här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Skapa en plot med endast fotbollsplaner i Gävle (se tips nedan)\n",
"\n",
"Tips:\n",
"* Du kan anropa `df[...]` med en lista av radindex (oftast radnummer)\n",
"* `df.ANDAMAL == 'fotbollsplan'` ger dig en lista med radindex där _ANDAMAL_ är lika med _fotbollsplan_"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgift 2.3.4: Skriva rapporter i Word och Excel med [`python-docx`](https://python-docx.readthedocs.io/) och [`openpyxl`](https://openpyxl.readthedocs.io/)\n",
"\n",
"Skapa ett Word-dokument och ett Excel-dokument med en kort rapport om idrottsplatserna i `idrottsplatser.geojson`, t.ex. antal platser av respektive typ. Välj själv om du använder `fiona` eller GeoPandas för att läsa filen. Använd själv respektive pakets dokumentation för att hitta hur du ska gå tillväga."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Avsnitt 2.3: Avslut\n",
"\n",
"Nu ska du få prova använda flera av de saker du lärt dig vid dessa övningar.\n",
"\n",
"Gör __en__ av följande uppgifter:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgiftsalternativ 2.3.1: GDS-BAL\n",
"\n",
"**OBS:** Denna uppgift måste genomföras på egen dator uppkopplad på intranätet!\n",
"\n",
"**OBS:** Kräver inloggningsuppgifter till _oe11gdlr_ databasen (GDSBAL verifikation)\n",
"\n",
"* Använd paketet [`cx_Oracle`](https://cx-oracle.readthedocs.io/en/latest/) och SQL för att hitta antalet gällande byggnader i databasen\n",
" * Använd `'(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oe11gdlr.lmv.lm.se)(PORT=1521))(CONNECT_DATA=(SID=oe11gdlr)))'` som serveradress och `encoding='utf-8'`\n",
" * Byggnader finns i tabellen `gds_bui.building` och de är gällande om `OBJECTSTATUS` är lika med `1`\n",
"* Skapa en enkel webbserver med `Flask` som visar antalet byggnader\n",
" * Börja med första kodstycket [här](https://flask.palletsprojects.com/en/1.1.x/quickstart/) och avsluta med raden `app.run()`\n",
" * När du kör koden kommer den inte att bli klar, utan starta den, vänta på meddelandet att servern är redo och besök sedan den addressen som visas i en ny flik i din webbläsare, avsluta sedan webbservern genom att välja Kernel/Interrupt i menyn\n",
"* Använd Pandas (utan Geo-biten denna gång) för att köra följande SQL:\n",
"```sql\n",
"SELECT OBJECTSTATUS, COUNT(*) FROM GDS_BUI.BUILDING GROUP BY OBJECTSTATUS\n",
"```\n",
" * Du kan köra SQL med Pandas genom att använda `pandas.read_sql('din SQL-fråga här', con=...resultatet från cx_Oracle.connect här...)`\n",
"* Gör det lättare att läsa resultatet genom att byta ut siffrorna i `OBJECTSTATUS` kolumnen mot passande text (0 är planerad, 1 är gällande, 2 är gällande under utredning och 3 är avregistrerad)\n",
"* Modifiera din webbserver för att visa statistiken för samtliga objektstatus\n",
"\n",
"Gör så många av dessa som du hinner:\n",
"\n",
"* Skriv om din webbserver så att du har en sida för byggnader och en för addressplatser (tabellen `gds_adr.addressplace`)\n",
"* Använd din DataFrame eller kör ett eget anrop med cx_Oracle för att skapa en Word-rapport över samtliga objektstatus för byggnad och adress\n",
"* Använd [detta officiella exempel](https://github.com/oracle/python-cx_Oracle/blob/master/samples/SpatialToGeoPandas.py#L170-L177) eller det förenklade exemplet nedan för att få in resultatet av följande fråga i en GeoDataFrame:\n",
"```sql\n",
"SELECT pl.postcode, pl.posttown, sdo_util.to_wkbgeometry(po.geometry) FROM GDS_ADR.addresspoint po\n",
"JOIN GDS_ADR.addressplace pl ON pl.id = po.addressplace_id\n",
"WHERE ROWNUM < 20\n",
"```\n",
"```python\n",
"cursor.execute({})\n",
"gdf = geopandas.GeoDataFrame(cursor.fetchall(), columns=[{}, 'wkbgeometry'])\n",
"gdf['geometry'] = geopandas.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x.read())))\n",
"del gdf['wkbgeometry']\n",
"```\n",
"* Plotta resultatet och gör det tillgängligt i din Word-rapport"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgiftsalternativ 2.3.2: Externa datakällor\n",
"\n",
"* Använd `requests` och den inbyggda modulen `zipfile` för att ladda ned och extrahera [befolkningsstatistik från SCB](https://www.scb.se/hitta-statistik/regional-statistik-och-kartor/geodata/oppna-geodata/statistik-pa-rutor/) (välj _Befolkning totalt_)\n",
"* Läs in det i GeoPandas och plotta det som en kloroplet-karta\n",
"* Skapa en enkel webbserver med `Flask` som visar den totala folkmängden i hela Sverige\n",
" * Börja med första kodstycket [här](https://flask.palletsprojects.com/en/1.1.x/quickstart/) och avsluta med raden `app.run()`\n",
" * När du kör koden kommer den inte att bli klar, utan starta den, vänta på meddelandet att servern är redo och besök sedan den addressen som visas i en ny flik i din webbläsare, avsluta sedan webbservern genom att välja Kernel/Interrupt i menyn\n",
"* Ladda ned och extrahera listan över [myndighetskontor från SCB](https://www.scb.se/hitta-statistik/regional-statistik-och-kartor/geodata/oppna-geodata/myndighets--och-kommunkontor/)\n",
"* Använd GeoPandas eller Shapely för att få en lista över samtliga myndighetskontor inklusive den lokala befolkningstätheten\n",
" * Tips: GeoPandas har funktion för spatiala joins, men detta kräver att paketet `rtree` är installerat. Om du är på Windows behöver måste du installera detta med `%conda install rtree` för att få med alla beroenden.\n",
"\n",
"Tips: Nedladdningar kan ta tid, här är ett perfekt tillfälle att använda IPython/Jupyters möjligheter till att använda celler. Lägg in koden för att ladda ned och extrahera respektive .zip-fil i en varsin cell, och resten av koden därefter så behöver du bara ladda ned och extrahera en gång.\n",
"\n",
"Gör så många av dessa som du hinner:\n",
"\n",
"* Skapa en Word- eller Excel-rapport över vilka myndighetskontor som ligger i de mest tätbefolkade områdena\n",
" * Avgör ditt val av Word eller Excel hur många myndighetskontor du tar med i rapporten?\n",
"* Skapa en plot av myndighetskontor, färglagd efter befolkningstäthet\n",
"* Lägg till någon av de plottar du gjort som en ny `route` i din webbserver\n",
"* Skapa en Word-rapport (eller lägg till i den du skapat tidigare) med någon av de plottar du gjort"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uppgiftsalternativ 2.3.3: TOPO\n",
"\n",
"**OBS:** Denna uppgift måste genomföras på egen dator uppkopplad på intranätet!\n",
"\n",
"* Använd paketet [`cx_Oracle`](https://cx-oracle.readthedocs.io/en/latest/) och SQL för att hitta antalet anläggningsområdespunkter i databasen\n",
" * Använd `'(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=topoprd.lmv.lm.se)(PORT=1521))(CONNECT_DATA=(SID=topoprd)))'` som serveradress och `encoding='utf-8'`, fråga Jan om inloggningsuppgifter\n",
" * Anläggningsområdespunkter finns i tabellen `TOPO.ANLAGGNINGSOMRADEP`\n",
"* Skapa en enkel webbserver med `Flask` som visar anläggningsområdespunkter\n",
" * Börja med första kodstycket [här](https://flask.palletsprojects.com/en/1.1.x/quickstart/) och avsluta med raden `app.run()`\n",
" * När du kör koden kommer den inte att bli klar, utan starta den, vänta på meddelandet att servern är redo och besök sedan den addressen som visas i en ny flik i din webbläsare, avsluta sedan webbservern genom att välja Kernel/Interrupt i menyn\n",
"* Använd Pandas (utan Geo-biten denna gång) för att köra följande SQL:\n",
"```sql\n",
"SELECT SUBTYP, COUNT(*) FROM TOPO.ANLAGGNINGSOMRADEP GROUP BY SUBTYP\n",
"```\n",
" * Du kan köra SQL med Pandas genom att använda `pandas.read_sql('din SQL-fråga här', con=...resultatet från cx_Oracle.connect här...)`\n",
"* Gör det lättare att läsa resultatet genom att byta ut siffrorna i `SUBTYP` kolumnen mot passande text (2841 för industri, 2842 för samhällsfunktion, 2843 för rekreation och 2844 för idrottsplan)\n",
"* Modifiera din webbserver för att visa statistiken för samtliga andamål\n",
"\n",
"Gör så många av dessa som du hinner:\n",
"\n",
"* Skriv om din webbserver så att du har en sida för punkter och en för ytor (tabellen `TOPO.ANLAGGNINGSOMRADEY`, `SUBTYP` 2831 för industri, 2832 för samhällsfunktion, 2833 för rekreation, 2834 för skjutfält, 2835 för idrottsplan)\n",
"* Använd din DataFrame eller kör ett eget anrop med cx_Oracle för att skapa en Word-rapport över samtliga ändamål för punkter och ytor\n",
"* Använd [detta officiella exempel](https://github.com/oracle/python-cx_Oracle/blob/master/samples/SpatialToGeoPandas.py#L170-L177) eller det förenklade exemplet nedan för att få in resultatet av följande fråga i en GeoDataFrame:\n",
"```sql\n",
"SELECT GANDRADAV, SUBTYP, sde.st_astext(SHAPE) FROM TOPO.ANLAGGNINGSOMRADEP WHERE ROWNUM < 20\n",
"```\n",
"```python\n",
"cursor.execute({})\n",
"gdf = geopandas.GeoDataFrame(cursor.fetchall(), columns=[{}, 'wktgeometry'])\n",
"gdf['geometry'] = geopandas.GeoSeries(gdf['wktgeometry'].apply(lambda x: loads(str(x))))\n",
"del gdf['wktgeometry']\n",
"```\n",
"* Plotta resultatet och gör det tillgängligt i din Word-rapport"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# din kod här"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment