Ing. Stanislav Šumbera, Ph.D.
Mendelova zemědělská a lesnická univerzita Brno
Zemědělská 3, 613 00
www: http://sumbera.com
E - mail ambrosa@mendelu.cz
PDF version (formated by author)
Raster data such as satellite images, air photos, scanned topographical maps or other types of imagery are becoming increasingly popular in GIS. It has been estimated that these data may represents more than 90% of the average GIS data holding by volume. One of the most used database to manage all kinds of data across the enterprise is Oracle. Oracle 9i brings its GIS technologies like Spatial option, interMedia or Database Workspace Manager for effective database storing of geographical-related data.
In this article, Oracle GIS technologies are used to build up an open-source prototype called "Image Storage" for storing rasters in Oracle9i database and displaying query results with help of MicroStation v8 Raster Manager.
Rastrová data (ať už mapové podklady, DMT modely, nebo snímky DPZ) tvoří až 90% průměrného množství GIS dat co se týče objemu. Efektivní správa rastrů proto představuje fundamentální funkčnost pro geografické systémy. Přesto je tomuto tématu stále věnována poměrně malá pozornost. Jednou z nejpoužívanějších databází pro správu heterogenních data je databáze Oracle. Verze Oracle 9i obsahuje vylepšené GIS technologie jako například Spatial, interMedia nebo Workspace Management pro efektivní ukládání geografických dat.
V tomto příspěvku jsou tyto technologie využity pro sestavení prototypu open-source aplikace "Image Storage", která do databáze Oracle rastry ukládá a v prostředí MicroStation v8 tyto rastry zobrazuje.
Systémy pro správu geografických rastrů můžeme rozdělit do třech typů (generací) :
1. Rastry jsou ukládány v proprietárním nebo publikovaném formátu do souborového systému.
2. Rastry jsou ukládány do proprietární rastrové "databáze" (Mr.SID, ECW).
3. Rastry jsou ukládány v publikovaném formátu do obecné databáze s prostorovým klíčem. Zakomponování výkonných kompresních algoritmů. např. RasDB, SDE, ImageStor apod.
Posledně uvedeným typ ukládání rastrů je aplikován v tomto příspěvku pro databázi Oracle 9i, která obsahuje řadu zajímavých vlastností pro správu prostorových dat. Namísto teoretických informací o možnostech této databáze je tento příspěvek zaměřen prakticky, tzn. byl vytvořen prototyp aplikace, která demonstruje práci s rastry v databázi Oracle 9i a možnosti zobrazování rastrů prostřednictvím MicroStation v8. Tento prototyp je volně k dispozici pro studijní a výzkumné účely.
Aplikace Image Storage pro management snímků umožňuje moderním způsobem ukládat, vyhledávat a vybírat rastrová data z databáze Oracle v jednom ze standardních rastrových formátů prostřednictvím Oracle Image Cartridge. Prostorová indexace a vyhledávání rastrů je založena na technologii Oracle Spatial. Rastrová data jsou spravována v tabulkách, nad kterými lze provádět dlouhé transakce prostřednictvím Oracle Workspace Manageru - tím je umožněno, aby se daný rastr nacházel v různých rozpracovaných stadiích (stavech) pro účely zpracování snímku. Zobrazení rastrů je umožněno v grafickém prostředí systému MicroStation.
Datová vrstva aplikace je reprezentována databází Oracle, konkrétně entitami vytvořenými pro management rastrů. Na logické vrstvě využívá Image Storage aplikačního rozhraní Javy pro komunikaci s Oraclem. Na úrovni prezentační je GUI aplikace vytvořeno v jazyku VC++ a s využitím tříd MFC. Pro zobrazování rastrů je využito knihovny Raster Manageru z MicroStation, viz obr1.
Obr. 1: Přehled architektury Image Storage.
Pro ukládání rastrových dat do databáze, jejich prostorovou lokalizaci a možnost managementu prostřednictvím dlouhých transakcí byly využity tři GIS technologie databázového serveru Oracle :
1. Image Cartridge
umožňuje ukládat, získávat a konvertovat rastrová data prostřednictvím objektového datového typu (ODT). Snímky se ukládají do databáze buď ve formě BLOB objektů - (binary large objects), nebo jako odkaz na souborový systém (objekty BFILE). Image Cartridge bylo využito jako základní technologie pro ukládání rastrů do databáze. Bližší specifikace technologie viz. firemní dokumentace ORACLE.
2. Spatial Cartridge
je představován souborem SQL funkcí a schémat, která umožňují ukládání, získávání, dotazování a aktualizaci prostorových objektů v databázi Oracle 9i . Tato technologie byla využita pro prostorovou lokalizaci rastru v databázi.
Objekty z těchto dvou cartridge jsou využity v tabulce aplikace img_storage (předpona je volitelná část), která obsahuje mimo jiné objekt ordimage, do kterého se ukládá rastr v podobě BLOBu a objekt sdo_geometry, ve kterém jsou uloženy geometrické souřadnice. Tabulka dále obsahuje primární klíč a název rastru:
create table img_storage (ID number PRIMARY KEY not null, NAME varchar2 (256), IMAGE ordsys.ordimage, MBR mdsys.sdo_geometry);
Pro tabulku je založen index a sekvence pro generování primárního klíče :
create index img_idx on img_storage(MBR) indextype is mdsys.spatial_index PARAMETERS('SDO_LEVEL = 8');
create sequence img_seq increment by 1;
Příklad prostorového vyhledávání rastrů:
A. Prostorový dotaz bez využití Spatial cartridge :
select name from img_storage where not ((x_min < -765900000 AND x_max < -765900000) or (x_min > -765600000 AND x_max > -765600000) or (y_min > -966000000 AND y_max > -966000000) or (y_min < -966100000 AND y_max < -966100000))
B. Tentýž prostorový dotaz s využitím Spatial Cartridge :
select a.id from img_storage a where sdo_filter(a.mbr,mdsys.sdo_geometry(2003,NULL,NULL, mdsys.sdo_elem_info_array(1,1003,3), mdsys.sdo_ordinate_array(-765900000,-966100000,-765600000,-966000000)), 'querytype=window')= 'TRUE'
3. Workspace Management (WM)
Technologie pro verzování tabulek. WM umožňuje, aby databáze udržovala rozdílné verze stejného záznamu (tj. řádku v tabulce) v jednom nebo více workspaces (prostředí, stavů). Uživatelé databáze potom mohou měnit tyto verze nezávisle. Tento přístup verzování v databázi má dvojí výhodu:
1. Verzování zdokonaluje konkurenční přístup k datům v databázi. Bez verzování jsou příslušné záznamy v případě konkurenčního přístupu uzamčeny.
2. Verzování umožňuje analýzy různých stavů dat současně, protože každá z těchto analýz pracuje nad separátní verzí dat.
Jednotkou verzování je databázová tabulka. Běžná tabulka může být transformována do verzovací tabulky, což znamená, že veškeré řádky v tabulce mohou podporovat různé verze dat. Z pohledu uživatele se tyto verze jeví jako jedna a táž tabulka, která se nachází v určitém workspace (stavu). Stav je virtuálním prostředím, které mohou sdílet uživatelé, aby do verzovaných tabulek prováděli změny (ORACLE 2001). Tyto Stavy mají mezi sebou hierarchickou strukturu: Každý Stav má svého předka a z každého Stavu lze odvodit potomka. Viz obr. 2.
Obr. 2: Hierarchická struktura Stavů. Základní Stav je vždy LIVE, z něj je v tomto případě odvozen Stav pro upload rastrových dat s názvem UPLOAD. Pro dočasné změny v rastru byl vytvořen Stav DRAFT, TEMP a BACKUP.
Logická vrstva aplikace je realizovaná prostřednictvím jazyka Javy. Zvolení tohoto jazyka bylo podmíněno především javovským rozhraním Oracle pro práci s rastry. Komunikace s Oracle databází se uskutečňuje prostřednictvím JDBC - Java Database Connectivity. Příkazy jsou vyvolávány statickým nebo dynamickým SQL. V aplikaci Image Storage je komunikace mezi logickou vrstvou a vrstvou prezentační řešeno pomocí JNI - Java Native Interface (viz dodatek)
Uživatelské rozhraní bylo napsáno v C++ s využitím MFC - Microsoft Foundation Classes a to z důvodu snadnější komunikace mezi kódem C++ a Javou. Kvůli zobrazování rastrů v prostředí MicroStation bylo uživatelské rozhraní napsáno jako DLL knihovna. Tato knihovna je v prostředí MicroStation zavedena pomocí aplikace napsané v MDL (MicroStation Development Language). Pokud ovšem zobrazování není podmínkou (např. je třeba pouze rastry do databáze ukládat) je možné spustit aplikaci pomocí samostatného EXE souboru. Kód uživatelského rozhraní (C++) komunikuje s logickou vrstvou (Java) prostřednictvím JNI - Java Native Interface. Přehled GUI je uveden v následující tabulce.
Popis funkčnosti | Uživatelské rozhraní |
Záložka pro připojení do databáze USER - název uživatele PASSWORD - heslo pro uživatele CONNECT - přihlášení do databáze DISCONNECT - odhlášení z databáze |
|
Záložka pro management tabulek CREATE - založení tabulky DROP - zrušení tabulky ENABLE VERSION - nastavení verzování pro tabulku DISABLE VERSION - zrušení verzování pro tabulku |
|
Záložka pro management Workspaces DO - vykonání příslušné operace nad Workspace Aktivní Workspace se nastaví kliknutím na požadované jméno Workspace |
|
Záložka pro upload rastrů do databáze OPEN - otevře se dialogové okno pro výběr souborů, které se budou ukládat do databáze UPLOAD ALL - spustí se ukládání (upload) |
|
Záložka pro download rastrů z databáze levé okno: zde se specifikuje SQL podmínka pro výběr rastrů např. prostorové omezení GET FENCE - podmínka se nastaví podle pozice ohrady v MicroStation DOWNLOAD - provede se download |
|
Záložka pro nastavení vlastnostíaplikace USE ORACLE SPATIAL : použije se vyhledávání podle prostorového klíče. DISPLAY DOWNLOADS : v prostředí MicroStation se rastry zobrazí CLIP FILES : v prostředí MicroStation se rastry ořežou dle ohrady. |
|
Příspěvek si kladl za cíl ukázat praktickou cestu jak ukládat rastrová data do databáze Oracle 9i s využitím nejmodernějších technologií jakými jsou bezesporu Workspace Manager, Image Cartridge a Spatial Cartridge. Uvedené příklady i zdrojový kód jsou k dispozici na adrese www.sumbera.com
JNI je technologie, která umožňuje kombinovat kód napsaný v Javě s kódem nativním. JNI je specifikováno korporací SUN MICROSYSTEMS. JNI technologie umožňuje časově náročnější operace nebo uživatelské rozhraní realizovat v nativním kódu pro daný operační systém, resp. naopak, část kódu realizovat v Javě. Alternativní možností k JNI je technologie J/Direct firmy Microsoft, jejíž hlavní výhodou je rychlost komunikace. Nativní kód je v prostředí Win32 reprezentovaný DLL knihovnou nebo EXE souborem. JNI umožňuje vyvolávat exportované funkce z DLL knihovny a naopak.
Prostřednictvím JNI je umožněno :
1. Spustit metodu třídy napsané v Javě
2. Spustit nativní funkci umístěnou v DLL
3. Manipulovat s javovskými objekty
4. Operovat s výjimkami
5. Zavádět javovské třídy
Přístup k nativní funkci z Javy
Příklad využití knihovny DLL a exportované funkce uvádí následující příklad:
class JavaJni{ static { System.loadLibrary("nativeLib"); // natáhne se knihovna s názvem nativeLib.dll } public native void callDllFunc(short limit); // deklarace nativní funkce ... }
Pro vytvoření implementace této nativní metody v DLL knihovně lze využít nástroj javah.exe s parametrem -jni a názvem třídy, ve které se deklarace nativní metody vyskytuje. Výsledkem bude deklarace nativní funkce v hlavičkovém souboru (přípona .h) pro nativní kód. Například při zadání : javah - jni JavaJni se vytvoří soubor "JavaJni.h" který bude obsahovat prototyp v C++ pro funkci callDllFunc:
#include <jni.h> extern "C" { JNIEXPORT jint JNICALL Java_JavaJni_callDllFunc (JNIEnv *jniEnv, jobject jObject, jshort param); // deklarace nativní funkce }
kde jniEnv je ukazatel na běžící virtuální stroj Javy, jObject představuje v tomto případě instanci objektu JavaJni a param je parametr funkce.
Přístup k metodě z Javy z nativního kódu
Rozšíříme výše uvedenou třídu JavaJni :
public class JavaJni { static JavaJni javaObj = new JavaJni(); public int onNativeCall(short limit){ // metoda volaná z nativního kódu... return true; } }
Pro volání z nativního kódu je třeba provést následující kroky:
1. Obdržet ukazatel na běžící virtuální stroj Javy
2. Získat ukazatel na JNI rozhraní
3. Získat třídu, kde je umístěna metoda
4. Získat identifikátory metody a objektu podle signatury a názvu metody vrácené z utility javap
5. Získat požadované instance objektu
6. Zavolat nestatické javovské metody pomocí příslušné funkce
Příklad implementace v nativním kódu je uveden v následujícím příkladu:
int JNICALL dll_javaMethodCall(short limit) { JavaVM *jvm; /* označuje Java viturální stroj */ JNIEnv *env; /* ukazatel na JNI */ jsize nVMs; /* počet vytvořených JVM */ /* 1.*/ jint res = JNI_GetCreatedJavaVMs(&jvm,(jsize)1,&nVMs); /* 2.*/ res = jvm->AttachCurrentThread(&env,NULL); /* 3.*/ jclass cls = env->FindClass("JavaJni"); /* 4.*/ jmethodID mid = env->GetMethodID(cls, "onNativeCall", "(S)I"); jfieldID fid = env->GetStaticFieldID(cls, "javaObj", "LJavaJni;"); /* 5.*/ jobject obj = env->GetStaticObjectField(cls, fid); /* 6.*/ return env->CallIntMethod(obj, mid,limit); }
/*-------------------------------------------------------------------------*/ public int downloadImage ( String pre, // table prefix String where, // where statement for SQL String path // download path ) /*-------------------------------------------------------------------------*/ // downloads all images specified by where condition { String select = "select IMAGE,NAME from "+pre+"_storage " + where; int counter = 0; try{ int index1 = 0; Statement s1 = con.createStatement(); OracleResultSet rs1 =(OracleResultSet)s1.executeQuery(select); while(rs1.next()){ OrdImage imgObj = (OrdImage) rs1.getCustomDatum(1,OrdImage.getFactory()); String fileName = rs1.getString(2); try { counter++; int index = fileName.lastIndexOf("\\"); String name = fileName.substring(index+1); String fullName = path + "\\"+name; try{ sendLog(">> Receiving data into file \n"); imgObj.getDataInFile(fullName); } catch (Exception e){ sendLog(">> " + e +"\n"); } loadFile(fullName,0); // native call sendLog("File downloaded : "+fullName+"\n"); } catch (Exception e) { sendLog(">> " + e +"\n"); } } rs1.close(); s1.close(); } catch(Exception e) { sendLog(">> " + e); } sendLog("Download successful \n"); return counter; }
/*-------------------------------------------------------------------------*/ public void uploadImage ( String pre, // Image table prefix String FileName, // Raster File Name to upload String sqlInsert, // inserting SQL statement String seq, // sequence number (ID) ) /*-------------------------------------------------------------------------*/ // upload one raster file into database { try { String sql; OraclePreparedStatement stmt; String name = FileName.substring(FileName.lastIndexOf("\\")+1); sql = "insert into "+pre+"_storage values "+ "("+ seq +",'"+ name +"',"+ sqlInsert+ ")"; stmt = (OraclePreparedStatement)con.prepareCall(sql); sendLog(">> Executing insert "+"\n"); try{ stmt.execute(); } catch (SQLException e) { sendLog(">> " + e); } stmt.close(); sql ="select IMAGE from "+pre+"_storage where NAME = '"+ +name+"' for update"; Statement s1 = con.createStatement(); OracleResultSet rs1 = (OracleResultSet)s1.executeQuery(sql); while(rs1.next()) { OrdImage imgObj = (OrdImage) rs1.getCustomDatum(1, OrdImage.getFactory()); sendLog(">> loading data from file ... \n"); imgObj.loadDataFromFile(FileName); sql ="update "+pre+"_storage set image = ? where NAME = '"+ name+"'"; sendLog(">> Executing update \n"); stmt = (OraclePreparedStatement) con.prepareCall(sql); stmt.setCustomDatum(1,imgObj); try{ stmt.execute(); } catch (SQLException e) { sendLog(">> " + e); } stmt.close(); sendLog("File "+name+" uploaded \n"); stmt.close(); } stmt.close() ; } catch(Exception e) { sendLog(">> " + e+ "\n"); } }