Igår så skapade jag lite förutsättningar för att spara punkter i geopackage-filer. Idag tänkte jag använda koden för att skapa en python-modul som går att importera i andra skript och därmed göra skriptet återanvändningsbart i flera framtida projekt.
Att komma från Basic-programmering eller motsvarande gör objektsorienterad programmering lite krångligt. Att skapa funktioner är en sak, men att bryta ut funktioner i moduler blir lite krångligare ändå.
När man tänker sig ett programmeringsflöde, så blir det lätt linjärt. Först skall något hända, sedan skall något annat hända, för att slutligen… och så vidare.
Att skapa moduler är inte alltid något användbart, men för en del funktioner så blir det mycket enklare att återanvända koden. En bonus kan också vara att huvudkoden blir lättare att läsa.
Moduler i Python är egentligen inget krångligt. Det är helt enkelt separata pythonfiler med egna definitioner och funktioner. Att importera en modul är inte svårare än att anropa filen:
import filnamn
Filändelsen *.py behöver inte anges. Om man har flera filer med olika definitioner så kan man spara dessa i en katalog och istället importera hela katalogen.
import katalog
Den enda förutsättningen i det här fallet är att det finns en ”__init__.py” fil i katalogen. Filen behöver inte innehålla någonting, men själva närvaron av filen talar om för Python att katalogen innehåller moduler.
Det krångliga för mig blir att tänka igenom modulerna och hur data i exempelvis variabler hanteras eller om man behöver överföra skapade objekt och anropa dessa på olika platser.
För min modifierade modul från gårdagen så tar jag koden direkt.
# -*- coding: utf-8 -*- """ Denna Pythonmodul har tre funktioner: - skapaDataset(EPSG[int], filnamn[string], filtyp[string] attributnamn[lista-string], attributtyp[list-ogr.OFTxxx]) - nyPunkt(datalager[ogr layer], X[tal], Y[tal], attributnamn[lista-string], attributvärde[lista]) - avslutaDataset(dataset) Funktionen "skapaDataset()" returnerar två objekt. - Ett dataset och - Ett lager Dessa kan skapas med exempelvis: mitt_dataset, mitt_lager = skapaDataset(.....) Använd sedan dessa i de övriga funktionerna tillsammans med egna värden och attribut. """ # Importera GDAL/OGR för att skapa punkter och spara geopackage from osgeo import ogr, osr # Funktionen skapar ett geopackage med en uppsättning attribut # EPSG är integer (ex 4326) # filnamn är en sökväg med namn (sätt filändelsen *.gpkg) # filtyp är ogr driver namn ex: "GPKG", "ESRI Shapefile" # attrNamn är en lista med texter som representerar attributnamnen # attrTyp är en lista med ogr.OFTString, ogr.OFTReal, ogr.OFTInteger, eller motsvarande. # Det är viktigt att det finns listorna är lika långa för Namn och Typ. # Funktionen returnerar en "tuple" med ett dataset och ett lager (ds, lr = skapaLager(...)) def skapaDataset(EPSG, filnamn, filtyp, attrNamn, attrTyp): gpkgDrv = ogr.GetDriverByName(filtyp) ds = gpkgDrv.CreateDataSource(filnamn) srs = osr.SpatialReference() srs.ImportFromEPSG(EPSG) lr = ds.CreateLayer(filnamn, srs, ogr.wkbPoint) for namn, typ in zip(attrNamn, attrTyp): fd = ogr.FieldDefn(namn, typ) lr.CreateField(fd) return(ds, lr) # Funktionen lägger till en punkt med position och attribut, till lager "lr". # Funktionen kan upprepas hur många gånger som helst, en gång för varje punkt # Viktigt att Namn och Varde listorna är lika långa def nyPunkt(lr, lon, lat, attrNamn, attrVarde): ld = lr.GetLayerDefn() ft = ogr.Feature(ld) punkt = ogr.Geometry(ogr.wkbPoint) punkt.AddPoint(lon, lat) ft.SetGeometry(punkt) for namn, varde in zip(attrNamn, attrVarde): ft.SetField(namn, varde) lr.CreateFeature(ft) # Funktionen stänger dataset "ds" def avslutaDataset(ds): ds.Destroy()
Nu kan jag importera min kod i andra skript och få tillgång till funktionerna:
- skapaDataset()
- nyPunkt()
- avslutaDataset()
Så ett enkelt skript för att skapa en punkt på position 15, 55 med värdet ”text” i attributet ”attribut”, i en fil med namnet ”test.gpkg” ser ut så här (min modul heter ”min_modul.py”):
from osgeo import ogr from min_modul import skapaDataset, nyPunkt, avslutaDataset mitt_dataset, mitt_lager = skapaDataset(4326, "test.gpkg", ["attribut"], [ogr.OFTString]) nyPunkt(mitt_lager, 15, 55, ["attribut"], ["text"]) avslutaDataset(mitt_dataset)
Sådär, ”svårare” är det inte…
Som den uppmärksamme kanske märker så har jag ändrat en del i koden från igår. Jag har gjort det möjligt att skapa punktlager av olika typ och inte bara GeoPackage, men du får själv hålla reda på vad ”drivern” heter. Nu använder jag inte heller globala variabler, utan skickar med ”objekten” till funktionerna när dessa skall skapas, avslutas eller när punkter skall läggas till.
Om detta är bra Python sed eller ej vet jag inte, men det fungerar! Det gör det dessutom möjligt för mig att skapa flera dataset med olika lager så att jag samtidigt kan skriva punkter till flera olika lager. Nu kan jag i ett enda skript skriva såväl färdloggen som spara enskilda punkter när jag passerar korsningar, broar, brunnslock, postlådor eller liknande.
Att sedan dessa ”loggar” direkt skapas i ett format som jag kan öppna i QGIS är definitift en bonus.
[EDIT]
Sedan jag skrev detta så har jag uppdaterat min kod och översatt allt till Engelska. Biblioteket finns numera på GitHub (länk) och består av en modul med på sikt flera pythonbibliotek.
Om du hämtar biblioteket så kan du importera allt, eller som tidigare enskilda funktinoer:
import gs_python from gs_python import geocreate from gs_python.geocreate import createDataset, addPoint, closeDataset help(gs_python) help(geocreate) help(createDataset)