niedziela, 28 października 2012

Kilka uwag o Python DBAPI

Nowa wersja QAZP2 jest już dostępna bezpośrednio albo z repozytorium wtyczek. Działa już edytor faktów kulturowych, to znaczy do każdego stanowiska można przyporządkować określenia jego przynależności chronologiczno-kulturowej.

Wymagania

Jednym z ważniejszych założeń, których staram się trzymać rozwijając program jest jego "bezobsługowość", czyli uwolnienie użytkownika od obowiązku dbania o zależności w postaci dodatkowych modułów, spoza biblioteki standardowej i w tym sterowników do bazy danych. Obecnie QAZP2 umożliwia korzystanie z bazy SQLite albo PostgreSQL, z tej prostej przyczyny, że tylko spośród tych dwóch można wybierać w QGIS-ie. Moduł do obsługi jest dostępny w bibliotece standardowej Pythona, natomiast instalator QuantumGIS, gdy jest uruchamiany pod kontrolą MS Windows, domyślnie dodaje sterownik psycopg do łączenia się z bazą PostgreSQL. W przypadku użytkowników Linuksa problem jest trochę bardziej skomplikowany, ale w takiej sytuacji zakładam, że potrafią oni korzystać z menadżera pakietów i zainstalować wymagane biblioteki.
Wszystko to wydaje się trochę zagmatwane, więc w skrócie sytuację można opisać następująco: dwa rodzaje bazy danych SQLite/Spatialite i PostgreSQL/Postgis i sterowniki sqlite3 oraz psycopg, które implementują wzorzec opisany w PEP 249.

Pobieranie informacji o połączeniu

Pierwszy z problemów, które w związku z tym trzeba było rozwiązać to określenie, z jakiej bazy korzysta użytkownik. Jak już wspominałem QAZP ma być "bezobsługowe", czyli między innymi automatycznie rozwiązywać problemy konfiguracji, np. takie jak wybór bazy, z której pobrać dane. W obecnej wersji obsługiwane są trzy warstwy wektorowe: Miejsca, czyli zbiór punktów charakterystycznych na mapie; Trasy - drogi pokonane w trakcie badań archeologicznych; Stanowiska - ślady dawnego osadnictwa. Każda z nich w QGIS jest reprezentowana przez obiekt klasy QgsVectorLayer. Z tego kolei można uzyskać informacje o źródle pochodzenia warstwy, którym może być plik SHP, usługa WFS, czy właśnie relacyjna baza danych. Wywołując metodę dataProvider() uzyskuje się obiekt klasy QgsVectorDataProvider, którego metoda dataSourceUri() zwraca ciąg reprezentujący połączenie. Może to być ścieżka do pliku, albo lokalizacja bazy z parametrami - nazwą użytkownika, hasłem, itp. Żeby uniknąć ręcznego parsowania takiej informacji można wykorzystać klasę QgsDataSourceURI, której metody host(), port(), database() i inne pozwalają pobrać potrzebne informacje do nawiązania połączenia z bazą danych. To może się wydawać skomplikowane, ale w rzeczywistości jest dość banalne i całą operację łączenia można załatwić w kilku liniach kodu, tak jak to jest zrobione w metodzie zrodla.getPolaczenie2(). Jak zwykle w takich przypadkach większość czasu zajmuje znalezienie rozwiązania problemu, a nie jego implementacja ;).

Parametry poleceń SQL

Wspomniana metoda w zależności od nazwy bazy łączy się z wykorzystaniem modułu sqlite3 albo psycopg. I tutaj z mojej perspektywy rozpoczyna się najgorsze - otóż instrukja PEP 249 dopuszcza kilka różnych sposobów podawania parametrów polecenia sql. W przypadku sqlite przykładowe zapytanie mogłoby mieć postać (1) select * from tabela where a=? and b=? albo (2) select * from tabela where a=:pa and b=:pb gdy posługujemy się parametrami nazwanymi. Natomiast używając psycopg polecenie (1) trzeba zapisać jako select * from tabela where a=%s, a (2) w postaci select * from tabela where a=%(pa)s and b=%(pb)s. Każdy z tych formatów jest poprawny z uwagi na PEP 249, ale z jakiegoś powodu autorzy sqlite3 zdecydowali się na jeden, a psycopg na drugi. Są dwa rozwiązania tego problemu:
  1. Każdorazowe badanie rodzaju bazy i na tej podstawie konstruowanie poleceń z odpowiednio zaznaczonymi parametrami.
  2. Skonstruowanie adaptera, który by "opakował" oryginalne połączenie sqlite3 albo psycopg i umożliwiał jednorodny sposób wykonywania poleceń.
Zdecydowałem się na drugą opcję i napisałem dwie klasy - Polaczenie i Polecenie. Pierwsza opakowuje połączenie sqlite3 albo psycopg i w metodzie prep(sql), znając rodzaj bazy dostosowuje polecenie SQL do jej wymagań. Jako format wejściowy zdecydowałem się na ten używany w sqlite3, gdyż wydaje mi się bardziej czytelny. Jeżeli połączenie dotyczy bazy PostgreSQL, to za pomocą wyrażenia regularnego :([0-9a-zA-Z_]+) i metody re.subn parametry są zamieniane na format z psycopg.

Nie jest komfortowym rozwiązaniem, gdy do napisania uniwersalnego kodu trzeba tworzyć dodatkowe klasy tak jak w powyższym przypadku. Choćby z tego powodu bardziej podoba mi się rozwiązanie JDBC albo choćby z Go. Tu można postawić zarzut, że przecież istnieje coś takiego jak ORM i cały mój wywód nie ma sensu. Postaram się na niego odpowiedzieć wkrótce.

sobota, 20 października 2012

Kontekst

Zanim zagłębię się w problemy technologiczne, chciałbym krótko nakreślić ich kontekst, czyli powód dla którego jest rozwijany program QAZP2. Historia rozpoczyna się w 2010 roku, kiedy w związku z rozpoczęciem projektu naukowego w Instytucie Prahistorii UAM, finansowanego ze środków Narodowego Instytutu Dziedzictwa, pojawiła się potrzeba ewidecjonowania wyników badań w relacyjnej bazie danych.  Ważnym składnikiem zapisywanych w niej informacji jest tak zwany komponent przestrzenny, czyli współrzędne geograficzne. W ten sposób wyniki badań mogą być analizowane w kontekście środowiska i krajobrazu, co ma się przyczyniać do lepszego wyjaśniania decyzji podejmowanych przez ludzi w przeszłości.
Jako kontener do przechowywania danych został wybrany system PostgreSQL wyposażony w rozszerzenie PostGIS, które to umożliwia zapisywanie informacji przestrzennych.
Biorąc pod uwagę marny stan finansów polskiej nauki i instytucji zainteresowanych wykorzystywaniem takich informacji do ochrony zabytków w Polsce podjęto decyzję, że wszystkie narzędzia powinny być bezpłatne i tym samym obniżyć próg wejścia. Dlatego do gromadzenia danych jest używany wspomniany PostgreSQL+PostGIS, a do ich wyświetlania program QuantumGIS (QGIS). Ta aplikacja umożliwia proste wprowadzanie danych przestrzennych i tekstowych, jednak ten mechanizm jest niewystarczający do wprowadzania szczegółowych informacji pochodzących z propspekcji terenowej. Rozbudowany formularz do wprowadzenia danych jest realizowany właśnie jako QAZP2.
Zgodnie z aktualnymi założeniami ma umożliwiać wprowadzanie informacji o miejscach, czyli punktach rejestrowanych za pomocą odbiornika GPS; trasach - czyli zapisu drogi, która została pokonana na ziemii albo w powietrzu w czasie wykonywania zdjęć lotniczych; stanowiskach - czyli odnalezionych śladach działalności człowieka - np. osadach, obozowiskach, osadach itp. Oprócz "świeżych" danych w bazie mają się znaleźć także archiwalne pochodzące z badań AZP prowadzonych na przestrzenii lat, które mniej więcej od ostatniej dekady XX wieku są zapisywane w postaci elektronicznej. Ważne w tym wszystkim jest niedestrukcyjny charakter badań, czyli to, że nie prowadzą one do zniszczenia śladów osdanictwa w trakcie wykopalisk, a główną przesłanką do ich prowadzenia, oprócz wyjaśniania przeszłości, jest ich efektywna ochrona przed zniszczeniem.
W obecnej wersji 0.6, która jest dostępna od Poniedziałku do bazy danych można już wprowadzać wszystkie zakładane typy informacji, czyli miejsca, trasy i stanowiska. 

sobota, 13 października 2012

Początek

Przed chwilą zatwierdziłem ostatnie zmiany w QAZP2, czyli nowej wtyczki do QuantumGIS, której zadaniem jest wspomaganie użytkowników w zarządzaniu informacjami zbieranymi w trakcie nieinwazyjnych badań archeologicznych. I pomyślałem, że być może warto opisać doświadczenia z realizacji tego projektu, przede wszystkim po to, by nie umknęły mej pamięci, ale też ze względu na to, że problematyka tworzenia rozszerzeń do QuantumGIS jest w polskojęzycznym internecie rzadko omawiana.
W związku z tym tworząc ten dziennik chciałbym poruszyć następujące kwestie:
  1. Wykorzystywanie API QuantumGIS.
  2. Tworzenie aplikacji z zastosowaniem biblioteki Qt.
  3. Zarządzanie danymi archeologicznymi i ich przetwarzanie. 
Na tą chwilę przychodzą mi do głowy tylko te trzy punkty, ale w każdej chwili lista może ulec rozszerzeniu. Będę starał się pisać na tyle regularnie, na ile pozwolą mi na to obowiązki zawodowe i czasami przemijające twórcze natchnienie ;). W ramach wstępu w kolejnym poście opiszę krótko projek, w ramach którego narodziła się idea utworzenia rozszerzenia QAZP(2)