Trzecia i ostatnia część cyklu poświęconego podstawom języka SQL. W pierwszej i drugiej części stworzyliśmy polecenie SQL, które na podstawie zdefiniowanych kryteriów wyszukuje w bazie danych stanowiska i powiązane z nimi fakty kulturowe (określenie okresu dziejów, jednostki kulturowej, funkcji a także znalezionych śladów działalności człowieka). To polecenie ma następującą postać:
SELECT S.ID, S.OBSZAR_AZP,
S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F
ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'
Wskazujemy w nim, że w zestawieniu ma znaleźć się identyfikator stanowiska, numer obszaru AZP, numer stanowiska w ramach obszaru AZP - te atrybuty są pobierane z tabeli Stanowiska oznaczonej literą 'S' oraz fukcję stanowiska - informacja zapisana w tabeli FaktyKulturowe. Pod uwagę są brane stanowiska z obszaru '55-12', które mają określoną funkcję i zostały przebadane między 1 stycznia 2012 i 31 grudnia 2012 roku.
Istotą wielu zestawień są obliczenia, które można także wykonywać przy pomocy języka SQL. Służą do tego tak zwane funkcje agregujące. Nazywamy je w ten sposób, ponieważ zasadą ich działania jest to, że zbierają wyniki z wierszy, które spełniają podane warunki i przetwarzają je w określony sposób.
Przykładem funkcji agregującej jest funkcja COUNT(nazwa_atrybutu), która oblicza ile razy pojawiła się wartość w kolumnie, której nazwę wpisaliśmy między nawiasami okrągłymi (w miejscu oznaczonym jako nazwa_atrybutu). Na przykład, jeżeli w poleceniu SQL użyjemy funkcji COUNT, a w jej parametrze podamy kolumnę id z tabeli stanowiska, to policzy wszystkie wiersze, które mają w tej kolumnie jakąś wartość.
SELECT COUNT(S.ID) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F
ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'
Wynik powyższego polecenia można zinterpretować jako liczbę stanowisk zapisanych w bazie danych z obszaru '55-12', które mają określoną funkcję i zostały przebadane między 1 stycznia 2012 i 31 grudnia 2012 roku. Warto zauważyć, że wynik takiego polecenia będzie zawsze zawierał dokładnie jeden wiersz z jedną kolumną, w które zostanie wyświetlona liczba stanowisk spełniających kryteria.
Innym przykładem funkcji agregującej jest SUM(nazwa_atrybutu), która dodaje do siebie wartości zapisane w kolumnie nazwa_atrybutu. Wartościami tymi muszą być liczby. Możemy jej użyć do obliczenia łącznej liczby fragmentów ceramiki znalezionych na stanowiskach.
SELECT SUM(F.CERAMIKA) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F
ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'
Ponownie wynikiem będzie dokładnie jeden wiersz zawierający sumę wartości z kolumny CERAMIKA w tabeli FaktyKulturowe, w wierszach, które spełniają podane kryteria.
W jednym poleceniu SQL można użyć więcej niż jednej funkcji agregującej. Na przykład:
SELECT COUNT(S.ID), SUM(F.CERAMIKA) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F
ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'
Wynikiem polecenia będzie dokładnie jeden wiersz z dwoma kolumnami. W pierwszej zostanie wyświetlona liczba stanowisk, w drugiej liczba fragmentów ceramiki, które zostały na nich znalezione. Inne przydatne funkcje agregujące to MIN - określa minimalną wartość w kolumnie, MAX - maksymalną wartość w kolumnie, AVG - oblicza średnią wartości w kolumnie.
Ważnym aspektem używania funkcji agregujących jest grupowanie wyników. Wyobraźmy sobie, że chcemy uzyskać liczbę stanowisk zapisanych w bazie danych z podziałem na jednostki kulturowe. W takim wypadku polecenie SQL powinno mieć taką postać:
SELECT F.KULTURA, COUNT(S.ID) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F
ON S.ID = F.STANOWISKO GROUP BY F.KULTURA
Na pierwszy rzut oka widać, że polecenie jest bardzo podobne do tych, które opisałem wcześniej. Mamy funkcję agregującą COUNT(S.ID), która policzy wszystkie stanowiska. Oprócz tego mamy atrybut KULTURA, według którego zostaną pogrupowane i policzone stanowiska. Wynik polecenia będzie zawierał dwie kolumny i tyle wierszy, ile różnych kultur zostało znalezionych w tabeli FaktyKulturowe. Do nazwy każdej kultury, w drugiej kolumnie wiersza będzie przypisana liczba oznaczająca liczbę stanowisk reprezentujących jednostkę z kolumny pierwszej.
Oprócz tego w poleceniu pojawiło się wyrażenie GROUP BY F.KULTURA. Używając grupowania w poleceniach sql trzeba jawnie podać, według której kolumny (albo wielu kolumn) mają być agregowane wyniki. Należy pamiętać, że w poleceniach grupujących, między słowami SELECT i FROM mogą znaleźć się tylko funkcje agregujące i nazwy atrybutów, według których są łączone wartości. Te same nazwy atrybutów muszą znaleźć się w wyrażeniu GROUP BY.
W ramach ćwiczenia spróbuj napisać polecenie obliczające średnią ilość fragmentów ceramiki na wszystkich stanowiskach w bazie danych z podziałem na numer obszaru AZP i porównaj to z prawidłową odpowiedzią poniżej.
SELECT S.OBSZAR_AZP, AVG(F.CERAMIKA) FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F
ON S.ID = F.STANOWISKO GROUP BY S.OBSZAR_AZP
Pokazywanie postów oznaczonych etykietą archeologia. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą archeologia. Pokaż wszystkie posty
czwartek, 22 maja 2014
środa, 21 maja 2014
Zestawienia: nieformalnie o podstawach języka SQL - część 2
W poprzednim poście przedstawiłem proste polecenie SQL, które wyświetla wszystkie stanowiska archeologiczne z przypisanymi do nich informacją o funkcji stanowiska (osada, cmentarzysko, itp.). Ma ono następującą postać:
SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO
Fragment S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA oznacza, że mają zostać pobrane wartości z następujących kolumn: identyfikator stanowiska, numer obszaru AZP, numer stanowiska w ramach obszaru AZP, funkcja stanowiska.
Fragment FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.ID oznacza, że polecenie SQL dotyczy tabeli Stanowiska oznaczonej skrótem 'S' oraz tabeli FaktyKulturowe oznaczonej jako 'F'. Spójnik LEFT OUTER JOIN łączący nazwy tabel spowoduje, że nawet jeżeli stanowisko nie ma określonego ani jednego faktu kulturowego, to i tak zostanie wyświetlone (wtedy w miejscu funkcji pojawi się pusta wartość). Z kolei pisząc ON S.ID = F.STANOWISKO, informujemy, że każdy fakt kulturowy z tabeli FaktyKulturowe, w kolumnie (atrybucie) STANOWISKO ma zapisany identyfikator stanowiska, do którego należy. Aby wyświetlić tylko te stanowiska, które mają conajmniej jeden fakt kulturowy zamiast spójnika LEFT OUTER JOIN należy użyć JOIN (zaznacz aby zobaczyć prawidłową odpowiedź).
Powyższe polecenie wyświetli wszystkie stanowisk oraz ich funkcje zapisane w bazie danych. Natomiast naszym celem jest pobranie tylko stanowisk z obszaru AZP 55-12. Żeby osiągnąć taki efekt, musimy sformułować kryterium, którego spełnienie będzie warunkiem wyświetlenia wiersza z wartościami. W opisanym przpadku zapiszemy je następująco:
WHERE S.OBSZAR_AZP = '55-12'
Oznacza ono tyle, że w każdym wierszu wartość w kolumnie OBSZAR_AZP tabeli Stanowiska zostanie porównana z numerem '55-12'. Jeżeli jest z nim identyczna, to takie stanowisko zostanie uwzględnione w wynikach polecenia. Jeżeli nie - zostanie pominięte.
Możemy rozbudować nasz warunek i dodać drugie kryterium - na liście z wynikami mają znaleźć się tylko stanowiska, które mają określoną jednostkę kulturową (KPL, Kultura łużycka, itp.). Żeby uzyskać taki efekt warunek powinien wyglądać tak:
WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL
Kryterium dotyczące obszaru AZP oraz kultury muszą być obowiązkowo spełnione dla każdego wiersza wyników, dlatego połączyliśmy je operatorem logicznym AND. Gdyby wystarczające byłoby spełnienie tylko jednego z wymagań, to zamiast AND powinniśmy użyć (per analogiam) operatora OR.
F.KULTURA IS NOT NULL (w wolnym tłumaczeniu: "Kultura nie jest nullem") oznacza, że wartość w kolumnie KULTURA w tabeli FaktyKulturowe. W odwrotnej sytuacji, gdyby zależało nam tylko na stanowiskach bez określonej kultury - powinniśmy napisać F.KULTURA IS NULL (tłum. "Kultura jest nullem").
Możemy też użyć innych operatorów matematycznych: większy, mniejszy lub równy, mniejszy, itd. Dodajmy do warunków kolejne kryterium - wszystkie stanowiska powinny być przebadane po 1 stycznia 2012 roku:
WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01'
W rezultacie otrzymaliśmy następujące polecenie:
SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01'
Zastanów się jak, wykorzystując dotychczasową wiedzę, zmienić powyższe polecenie, aby otrzymać tylko stanowiska z okresu między 1 stycznia 2012 i 31 grudnia 2012 i porównaj z prawidłową odpowiedzią (zaczernioną).
SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'
c.d.n
SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO
Fragment S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA oznacza, że mają zostać pobrane wartości z następujących kolumn: identyfikator stanowiska, numer obszaru AZP, numer stanowiska w ramach obszaru AZP, funkcja stanowiska.
Fragment FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.ID oznacza, że polecenie SQL dotyczy tabeli Stanowiska oznaczonej skrótem 'S' oraz tabeli FaktyKulturowe oznaczonej jako 'F'. Spójnik LEFT OUTER JOIN łączący nazwy tabel spowoduje, że nawet jeżeli stanowisko nie ma określonego ani jednego faktu kulturowego, to i tak zostanie wyświetlone (wtedy w miejscu funkcji pojawi się pusta wartość). Z kolei pisząc ON S.ID = F.STANOWISKO, informujemy, że każdy fakt kulturowy z tabeli FaktyKulturowe, w kolumnie (atrybucie) STANOWISKO ma zapisany identyfikator stanowiska, do którego należy. Aby wyświetlić tylko te stanowiska, które mają conajmniej jeden fakt kulturowy zamiast spójnika LEFT OUTER JOIN należy użyć JOIN (zaznacz aby zobaczyć prawidłową odpowiedź).
Powyższe polecenie wyświetli wszystkie stanowisk oraz ich funkcje zapisane w bazie danych. Natomiast naszym celem jest pobranie tylko stanowisk z obszaru AZP 55-12. Żeby osiągnąć taki efekt, musimy sformułować kryterium, którego spełnienie będzie warunkiem wyświetlenia wiersza z wartościami. W opisanym przpadku zapiszemy je następująco:
WHERE S.OBSZAR_AZP = '55-12'
Oznacza ono tyle, że w każdym wierszu wartość w kolumnie OBSZAR_AZP tabeli Stanowiska zostanie porównana z numerem '55-12'. Jeżeli jest z nim identyczna, to takie stanowisko zostanie uwzględnione w wynikach polecenia. Jeżeli nie - zostanie pominięte.
Możemy rozbudować nasz warunek i dodać drugie kryterium - na liście z wynikami mają znaleźć się tylko stanowiska, które mają określoną jednostkę kulturową (KPL, Kultura łużycka, itp.). Żeby uzyskać taki efekt warunek powinien wyglądać tak:
WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL
Kryterium dotyczące obszaru AZP oraz kultury muszą być obowiązkowo spełnione dla każdego wiersza wyników, dlatego połączyliśmy je operatorem logicznym AND. Gdyby wystarczające byłoby spełnienie tylko jednego z wymagań, to zamiast AND powinniśmy użyć (per analogiam) operatora OR.
F.KULTURA IS NOT NULL (w wolnym tłumaczeniu: "Kultura nie jest nullem") oznacza, że wartość w kolumnie KULTURA w tabeli FaktyKulturowe. W odwrotnej sytuacji, gdyby zależało nam tylko na stanowiskach bez określonej kultury - powinniśmy napisać F.KULTURA IS NULL (tłum. "Kultura jest nullem").
Możemy też użyć innych operatorów matematycznych: większy, mniejszy lub równy, mniejszy, itd. Dodajmy do warunków kolejne kryterium - wszystkie stanowiska powinny być przebadane po 1 stycznia 2012 roku:
WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01'
W rezultacie otrzymaliśmy następujące polecenie:
SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01'
Zastanów się jak, wykorzystując dotychczasową wiedzę, zmienić powyższe polecenie, aby otrzymać tylko stanowiska z okresu między 1 stycznia 2012 i 31 grudnia 2012 i porównaj z prawidłową odpowiedzią (zaczernioną).
SELECT S.ID, S.OBSZAR_AZP, S.NR_AZP, F.FUNKCJA FROM Stanowiska S LEFT OUTER JOIN FaktyKulturowe F ON S.ID = F.STANOWISKO WHERE S.OBSZAR_AZP = '55-12' AND F.KULTURA IS NOT NULL AND S.DATA_BADAN >= '2012-01-01' AND S.DATA_BADAN <= '2012-12-31'
c.d.n
wtorek, 15 stycznia 2013
Budowanie wykazów nazw
Ostatnimi czasy dużo się działo w kwestii QAZP2. Ostatnie poprawki błędów, niekiedy dość poważnych, nowe funkcjonalności - na przykład wykonywanie skryptów modyfikujących zawartość bazy danych - co może mieć znaczenie, gdy osoba odpowiadająca za techniczny aspekt bazy połączonej z QAZP2 nie ma do niej bezpośredniego dostępu. W takim wypadku wystarczy spreparować skrypt SQL i wysłać do użytkownika, który za pomocą interfejsu graficznego aplikuje przygotowane zmiany. Rzecz jasna taki mechanizm jest pewnym zagrożeniem dla stanu bazy danych, jednak jest to koszt, który można ponieść, pamiętając, by nie stosować skryptów z niepewnego źródła. Między innymi z tego powodu trudno było mi znaleźć czas i spełnić obietnicę, że nowe artykuły będą wprowadzane conajmniej raz na tydzień.
Ostatnimi czasy na blogu była poruszana kwestia wykazów pojęć archeologicznych, których używa się do klasyfikacji kulturowo-funkcjonalno-historycznej. W QAZP2 wykorzystywane są do tego wykazy przygotowane w Narodowym Instytucie Dziedzictwa i uzupełnione o nazwy, których obecności wymaga praktyka badań archeologicznych. Ich konstrukcję próbowałem opisać w poprzednim artykule, natomiast dzisiaj chciałbym przedstawić inne podejście, które chociaż nie zostało wdrożone, to wydaje się ciekawą alternatywą dla wyżej wspomnianego sposobu budowania wykazów.
niedziela, 30 grudnia 2012
Klasyfikacja kulturowo-historyczna: kodowanie pojęć
W poprzednim artykule opisałem organizację wykazów do kodowania informacji o klasyfikacji kulturowo-historycznej. Dla przypomnienia: na ekranie aplikacji, albo na karcie AZP pokazywane są informacje o tym, z jakiego okresu dziejów (epoka kamienia, średniowiecze, ...), zdaniem archeologa, pochodzi badane stanowisko, jaka była jego funkcja (osada, cmentarzysko, ...), czy sposób i tradycję wykonania znalezionych przedmiotów (kultura pucharów lejkowatych, kultura łużycka, ...). Nazwy wykorzystywane do opisu są przechowywane w tabelach-wykazach: OKRESY_DZIEJOW, JEDNOSTKI, FUNKCJE. Kluczem podstawowym każdej krotki w tabeli wykazu jest ciąg alfanumeryczny kodujący informację, która jest zapisana w rekordzie. W przypadku wykazu funkcji kod ma następującą strukturę ABCCD, gdzie kolejne litery oznaczają jego segmenty. W tym przypadku obowiązkowe jest podanie tylko segmentu A, w którym określa się ogólną funkcję: np. obrzędową literą B, obronną literą O, gospodarczą G, itd. Zatem gdyby w opisie funkcji stanowiska podać kod składający się z litery G, oznaczałoby to tylko tyle, że pełniło ono w przeszłości funkcję gospodarczą. Kolejne segmenty kodu służą do zapisu bardziej szczegółowej informacji. Jeżeli kod S oznacza funkcję sepulkralną (związaną z grzebaniem zmarłych), to SL będzie oznaczało cmentarzysko ciałopalne, SL03 - cmentarzysko ciałopalne płaskie, a SL03J - cmentarzysko ciałopalne płaskie z grobami jamowymi, które to pojęcie jest przykładem najbardziej szczegółowej informacji w wykazie funkcji. Oczywiście sposób konstruowania kodu, opisany na przykładzie funkcji, został zastosowany w wykazie okresów i jednostek kulturowych.
Jak z każdym kodowaniem informacji, z tym także wiążą się określone zyski i straty, których bilans powinien decydować o jego zastosowaniu do naszego projektu. Do oczywistych zalet oczywiście należy spójna i czytelna reprezentacja informacji w bazie danych. Zamiast w każdym rekordzie charakteryzującym funkcję stanowiska pisać cmentarzysko ciałopalne płaskie z grobami jamowymi można po prostu wstawić kod SL03J i liczyć na to, że będzie on zrozumiały dla odbiorcy. Po drugie znacząco może ułatwić wyszukiwanie informacji o stanowiskach przy pomocy operatora SQL LIKE. Listę stanowisk z funkcją gospodarczą można utworzyć stosując polecenie SELECT * FROM FAKTY WHERE FUNKCJA LIKE 'G%', które uzwględni kod G, GI, GP13 i inne, które pasują do podanego wzorca. Szybkość wykonania takiego polecenia nie jest najwyższa, ale można je zoptymalizować na przykład przez utworzenie odpowiedniego indeksu na kolumnie FUNKCJA. Stosowanie wzorców nie zawsze będzie miało pożądanych skutek, ponieważ np. polecenie SELECT * FROM FAKTY WHERE FUNKCJA LIKE '%04%' zwróci stanowiska z funkcją osada otoczona rowem/rowami (MS04) a także grób szkieletowy katakumbowy (SS04).
Stosowanie kodów wymusza też na autorach pewną ścisłość w charakteryzowaniu stanowisk. Można to postrzegać jako zaletę, gdyż podwyższa to wartość bazy jako źródła archeologicznego, a także jako wadę, gdyż procesy społeczne nie zawsze można wtłoczyć w zmatematyzowany schemat. Jeżeli wykaz można rozszerzać o nowe pojęcia, to w przypadku, gdy brakuje odpowiedniego, można je dodać do tabeli. Natomiast w przypadku, gdy uzupełnianie wykazu jest niemożliwe, to trzeba próbować wykorzystać już istniejące, co może budzić oczywisty sprzeciw wobec utraty informacji.
niedziela, 16 grudnia 2012
Klasyfikacja funkcjonalno-kulturowo-chronologiczna: wykazy
Ostatnio sporo się działo w kwestii rozwoju QAZP2. Z radością donoszę, że wtyczka ma już zaimplementowane wszystkie funkcje, których wymagał program badawczy, w ramach którego aplikacja jest realizowana. Tym samym na podstawie zawartości bazy danych można wygenerować karty AZP zgodne ze specyfikacją Ministra Kultury i Dziedzictwa Narodowego.
Jednym z najważniejszych elementów karty AZP jest określenie dla znalezionych śladów działalności człowieka konotacji kulturowo-historycznej, czyli innymi słowy dokonanie klasyfikacji funkcjonalno-kulturowo-chronologicznej w oparciu o zebrane fakty, czyli na przykład fragmenty ceramiki, znalezione narzędzia i inne przedmioty, które archeologowi pozwalają określić wiek badanych osad, cementarzysk, itp. Aby przekaz był uniwersalny i czytelny dla każdego naukowca w Polsce do jego formułowania posługujemy się wykazami pojęć opracowanymi w Narodowym Instytucie Dziedzictwa. Taka standaryzacja ma niebagatelne znaczenie, gdyż czyni bazę danych uniwersalnym źródłem informacji nie tylko w ramach projektu, z którego pochodzi, ale dla wszystkich zainteresowanych analizą wydarzeń, które miały miejsce w przeszłości na terenach, z których pochodzą zebrane dane.
Opracowano trzy zbiory zawierające pojęcia służące do określania chronologii (okresu dziejów, z których pochodzi obiekt osadniczy - na przykład epoka kamienia, epoka brązu, średniowiecze, nowożytność); jednostki kulturowej, która informuje, że dany przedmiot wykazuje podobieństwo w ramach pewnej grupy wskaźników; funkcji - określenie roli obiektu - na przykład cmentarzysko, osada, grodzisko, itd. Każdy z wykazów jest zapisywany w osobnej tabeli, gdyż ich struktura jest odmienna. Konstrukcja schematu bazy danych obsługiwanego przez QAZP2 wynika z tego, że ma ona dwa podstawowe zadania:
- dostarczać informacje w celu ich wyświetlania,
- dostarczać informacje w celu ich przetwarzania i analizy.
Określenia każdej z właściwości (chornologii, jednostki kulturowej i funkcji) można dokonać na kilku poziomach szczegółowości. Na przykładzie chronologii wyróżnić można takie dwa: pierwszy poziom - epoka - na przykład epoka kamienia, epoka żelaza oraz drugi poziom - okres epoki - paleolit (czyli starsza epoka kamienia), II okres epoki brązu, Halsztad C (okres epoki żelaza). Użytkownik zgodnie ze swoimi umiejętnościami, wiedzą i możliwościami określa w ten sposób chronologię dla każdego znalezionego przedmiotu używając pojęć z pierwszego poziomu (mniej szczegółowo) albo drugiego (bardziej szczegółowo). Ma to znaczenie z uwagi na analizę danych, gdyż okres wpływów rzymskich różni się od okresu halsztackiego. Natomiast w przypadku wyświetlania, zwłaszcza w formie karty KEZA, która w dobie współczesnych technologii jest już tylko wyrazem zacofania polskich służb konserwatorskich, jest mniej znaczące. Stąd w każdej tabeli wykazów znajduje się pole skrót zawierajace wartość, która potem jest umieszczana na wydruku. Z kolei w kolumnie NAZWA zapisuje się wartości, które nie są pełną nazwą okresu chronologicznego, ale wyświetlona jest zrozumiała dla archeologa. Na przykład pisząc wartość HA C domyślamy się, że chodzi o Epokę żelaza - okres halsztacki C, z kolei nazwa Grupa mątewska wskazuje na neolityczną kulturę pucharów lejkowatych i to jest trzeci poziom dokładności w określeniu jednostki kulturowej.
Taka konstrukcja jest wygodna w analizie danych, gdyż wszukując wszystkie ślady związane z okresem Neolitu nie trzeba znać ich szczegółowego oznaczenia, czyli zbędna jest wiedza, czy osada wiąże się z kulturą pucharów lejkowatych, kulturą amform kulistych, czy grupą radziejowską, gdyż wszystkie mieszczą się w zbiorze Neolit. Z kolei szukając faktów dotyczących grupy mątewskiej nie zajmujemy się kulturą ceramiki wstęgowej, ani pozostałymi grupami kultury pucharów lejkowatych.
niedziela, 18 listopada 2012
Programowanie w Qt4: drukowanie tabel
Obecnie na "tapecie" mam problem generowania Kart Ewidencji Zabytków Archeologicznych (w skrócie KEZA), która jest formą archiwizowania informacji pochodzących z badań archeologicznych. Jej koncepcja wiąże się z początkami programu Archeologicznego Zdjęcia Polski, którego celem jest inwentaryzacja śladów osadnictwa w celu zapewnienia im ochrony konserwatorskiej. Jej wzór zmieniał się z biegiem lat, przy czym najważniejszym założeniem było to, by wszystkie informacje mieściły się na jednej kartce A4, co miało uzasadnienie w czasach "analogowych" metod archwizowania danych (to znaczy polegających na maszynowym wypełnianiu kart i wkładaniu do teczek czy segregatorów). Ponieważ do sprawozdania z badań archeologicznych muszą zawsze być dostarczone wydrukowane karty KEZA, to oczywiste jest, że QAZP2 musi także udostępniać taką funkcjonalność.
W najprostszej postaci wszystko czego potrzebujemy do drukowania przy pomocy Qt4 to dwie klasy: QPainter oraz QPrinter. Przykład ich zastosowania może wyglądać następująco:
drukarka = QPrinter()
drukarka.setOutputFormat(QPrinter.PdfFormat) # rodzaj drukarki
drukarka.setOutputFileName("nowy_plik.pdf")
painter = QPainter()
painter.begin(drukarka)
painter.drawText(10, 10, 'Test') # wydrukowanie tekstu
drukarka.newPage() # wysuniecie pierwszej strony
painter.drawText(10, 10, 'Test 2') # tekst na drugiej stronie
painter.end()W w takiej formie drukowanie nie wiąże się z żadną filozofią: klasa QPrinter reprezentuje uniwersalny interfejs urządzenia, który umożliwia drukowanie do pliku PDF, na kartkach papieru i inne. Z kolei QPainter wykorzystując ten kanał wspomaga programistę w pisaniu na "urządzeniu" tekstu, rysowaniu figur geometrycznych albo drukowaniu obrazków.
Ponieważ karta KEZA to faktycznie tabela, to powższy kod byłby znacząco bardziej skomplikowany i zwiększający prawodopodobnieństwo wystąpienia błędów. Aby procedura drukowania była trochę bardziej abstrakcyjna, to QAZP2 zamiast klasy QPainter została wykorzystana inna - QTextDocument, która jak sama nazwa wskazuje jest używana do reprezentowania dokumentów. Te mogą być tworzone przez użytkownika, który wpisuje zdania w polu tekstowym, albo automatycznie przy pomocy klasy QTextCursor, która służy do określania miejsca, w którym bieżący dokument jest edytowany i wstawiania do niego różnych obiektów - ciągów znaków, tabel, obrazków, których kształ można określać za pomocą znaczników HTML. Gdy dokument jest już gotowy wystarczy wywołać funkcję
print_(drukarka) klasy QTextDocument do wysłania go na urządzenie wskazane w parametrze drukarka. W tej metodzie zawartość dokumentu jest tłumaczona na ciąg poleceń takich jak w przykładzie powyżej.
To podejście w odróżnieniu od "niskopoziomowego" ma tą zaletę, że pozwala się skupić na zawartości dokumentu i jego wyglądzie. To w jaki sposób zostanie ona przełożona na papier albo plik PDF pozostaje w gestii klasy QTextDocument. Jak zostało ono wykorzystane w przypadku generowania karty KEZA opiszę następnym razem.
sobota, 10 listopada 2012
Przepis na PostGIS
Podstawą działania QAZP2 jest przestrzenna baza danych. Informacje o śladach działalności człowieka, a także o prowadzonych badaniach archeologicznych łączy się z przestrzenią po przez dodanie do nich współrzędnych geograficznych. W przypadku archeologii jest to kluczowa informacja, gdyż umożliwia analizę danych w kontekście krajobrazu i środowiska, które prawdopodobnie miały niebagatelny wpływ na decyzje podejmowane przez dawnych osadników.
Zanim przejdę do omówienia schematu bazy danych, do którego dostosowany jest QAZP2, w kilku zdaniach opiszę przygotowywanie systemu PostgreSQL do pracy z bazami przestrzennymi. Nie zamierzam szczegółowo omawiać poszczególnych poleceń - lepiej ode mnie robi to bogata dokumentacja. Zgodnie z tytułem - to ma być przepis, który szybko pozwoli upiec ciastko ;). Będę pisał z perspektywy użytkownika Linuksa, a dokładnie Debiana i zakładam, że czytelnicy posiadają podstawową wiedzę na temat posługiwania się tym systemem.
1. Instalacja i konfiguracja PostgreSQL
# apt-get install postgresql postgis
To polecenie chyba nie wymaga komentarza. Po kilku albo kilkunastu minutach oczekiwania (w zależności od prędkości połączenia) aktualna wersja oprogramowania - czyli system PostgreSQL i jego rozszerzenie Postgis powinno znaleźć się na dysku i zostać zainstalowane i uruchomione.
# cd /etc/postgresql/{wersja}/main/
# cp pg_hba.conf pg_hba.bak
# echo "local all all password" >> /etc/postgresql/{wersja}/main/pg_hba.conf
# /etc/init.d/postgresql restart
W domyślnej konfiguracji z bazą może się połączyć użytkownik root albo specjalnie tworzony do tego celu postgresql. A więc przed nawiązaniem połączenia z bazą i wykonaniem polecenia SELECT, UPDATE, CREATE itp. należy zalogować się jako root albo postgresql (ten drugi ma zdefiniowane domyślne hasło postgresql). Osobiście wolę inne podejście, w którym użytkownika i hasło podaje się w chwili łączenia z bazą danych. Ma ono dodatkowe uzasadnienie: w sytuacji, w której będziemy próbowali się połączyć w QGIS z bazą jako inny użytkownik, na przykład milosz, w domyślnej konfiguracji Postgre odrzuci nasze rządanie. Dlatego do pliku pg_hba.conf dodajemy następujący wiersz
local all all password (trzecia linia). Żeby zabezpieczyć się przed uszkodzeniem tego pliku, wcześniej (druga linia) należy wykonać jego kopię zapasową. Ta operacja wymaga zrestartowania systemu, co czyni polecenie w lini czwartej.
To jest najprostsza konfiguracja, jaką można sobie wyobrazić, ale w zupełności wystarcza do lokalnego testowania i korzystania z systemu PostgreSQL.
# createuser -U postgresql -W -d -P tester
Na koniec tworzymy nowego użytkownika o nazwie tester, który będzie uprawniony do tworzenia nowych baz danych (przełącznik -d). Przełącznik -P oznacza, że w chwili tworzenia użytkownika zostaniemy poproszeni o nadanie mu hasła. Przełączniki -U i -W są standardowe dla każdego polecenia PostgreSQL. Pierwszym wskazujemy tego użytkownika, jako który łączymy się do bazy w celu utworzenia nowego - w tym przypadku tym użytkownikiem jest wspominany już postgresql; użycie drugiego spowoduje, że przed dodaniem nowego, trzeba będzie podać hasło użytkownika postgresql.
2. Tworzenie bazy PostGIS
# createdb -O tester -h localhost -p 5432 -U tester -W azp2
# createlang plpgsql -U tester -W azp2
Kiedy nowy użytkownik jest już dodany, czas na utworzenie bazy danych. W pierwszym poleceniu tworzymy nową bazę o nazwie azp2, której właścicielem będzie tester (przełącznik -O). Po wykonaniu drugiej komendy w bazie azp2 będzie można tworzyć procedury w języku PL/SQL. Przełączniki -W i -U mają takie samo znaczenie, jak przy dodawaniu nowego użytkownika.
# cd /usr/share/postgresql/{wersja_postgre}/contrib/postgis-{wersja_postgis}
# psql -f postgis.sql -U tester -W azp2
# psql -f spatial_ref_sys.sql -U tester -W azp2
Na koniec do bazy azp2 trzeba dodać procedury, których używamy np. do dodawania współrzędnych, wyszukiwania na ich podstawie, itd., które są zaimplementowane w języku PL/SQL, o którym pisałem powyżej. Polecenia, które dodają wspomniane procedury znajdują się w katalogu utworzonym w chwili instalacji Postgis. Przechodzimy do niego w pierwszej lini, a następnie wywołujemy narzędzie psql podając jako parametr plik postgis.sql, który zawiera definicje procedur SQL. Do pracy z przestrzenną bazą danych przydatne będzie także dodanie definicji systemów odniesienia, które są używane do konwertowania współrzędnych. To także robimy za pomocą polecenia psql podając jako parametr plik spatial_ref_sys.sql.
I tyle. Baza przestrzenna jest gotowa do pracy. Od tej chwili można już tworzyć tabele ze współrzędnymi geograficznymi. Ale o tym innym razem.
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:
- Każdorazowe badanie rodzaju bazy i na tej podstawie konstruowanie poleceń z odpowiednio zaznaczonymi parametrami.
- Skonstruowanie adaptera, który by "opakował" oryginalne połączenie sqlite3 albo psycopg i umożliwiał jednorodny sposób wykonywania poleceń.
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.
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.
Subskrybuj:
Posty (Atom)