UNIVERZITA KARLOVA V PRAZE Přírodovědecká fakulta Katedra aplikované geoinformatiky a kartografie
ONLINE PLÁNOVÁNÍ NEJVÝHODNĚJŠÍ TRASY S VYUŽITÍM OPEN-SOURCE APLIKACÍ Bakalářská práce
Jiří Matyáš
srpen 2011
Vedoucí bakalářské práce: Mgr. Michal Schneider
Vysoká škola: Univerzita Karlova v Praze Katedra: Aplikované geoinformatiky a kartografie
Fakulta: Přírodovědecká Školní rok: 2010/2011
Zadání bakalářské práce
pro
Jiřího Matyáše
obor
Geografie a kartografie
Název tématu: Tvorba interaktivní mapy s plánovačem cyklotras s využitím knihovny pgRouting
Zásady pro vypracování Hlavním cílem předkládané bakalářské práce je vytvoření webové mapové aplikace, která na základě zadaných parametrů naplánuje průjezd územím z bodu A do bodu B nad vrstvou cyklotras. Aplikace bude sloužit k určení nejvhodnější trasy mezi dvěma body s možností nastavení několika průjezdních bodů. Součástí
bude možnost zvolit parametry trasy, mezi které bude patřit zejména náročnost terénu a kvalita povrchu cesty. Na základě těchto parametrů bude stanovena průměrná rychlost, jakou je možné po daném úseku cesty jet, a také například nezpevněné cesty budou vyloučeny z hledání trasy pro silniční kolo. Vzhledem k tomu, že parametry průjezdnosti jednotlivých úseků cyklotras budou zjištěny v terénu, modelové území pro aplikaci nebude nijak velké, ale bude dostatečně rozmanité, aby se na tomto území daly testovat různé varianty vyhledávání nejvýhodnější trasy. K tvorbě webové mapové aplikace bude použit databázový systém PostgreSQL s extenzí PostGIS, která umožní databázovému systému pracovat s prostorovými daty. O naplánování nejvýhodnější trasy se postará knihovna pgRouting. Vizualizace výsledné trasy bude zajištěna pomocí vybraného mapového
serveru.
Výsledná
aplikace
bude
zpřístupněna
na
serveru
geo.natur.cuni.cz. Rozsah průvodní zprávy: cca 30 stran Vedoucí bakalářské práce: Mgr. Michal Schneider Konzultant bakalářské práce: Datum zadání bakalářské práce:
1.11. 2010
Termín odevzdání bakalářské práce: červen 2011
Platnost tohoto zadání je po dobu jednoho akademického roku.
……………………………… Mgr. Michal Schneider Vedoucí bakalářské práce
V Praze dne: 2. 11. 2010
.............……………………… Ing. Markéta Potůčková, Ph.D. Vedoucí katedry
Prohlašuji, že jsem tuto bakalářskou práci vypracoval samostatně a že jsem všechny použité prameny řádně citoval. Jsem si vědom toho, že případné použití výsledků, získaných v této práci, mimo Univerzitu Karlovu v Praze je možné pouze po písemném souhlasu této univerzity. Svoluji k zapůjčení této práce pro studijní účely a souhlasím s tím, aby byla řádně vedena v evidenci vypůjčovatelů.
V Pardubicích dne 7.8.2011
……………………………… Jiří Matyáš
Poděkování Na tomto místě bych rád poděkoval vedoucímu mé práce Mgr. Michalu Schneiderovi za cenné rady a připomínky a za věnovaný čas. Dále bych chtěl poděkovat Katedře aplikované geoinformatiky a kartografie za zapůjčení knihy PostgreSQL: Praktický průvodce. V neposlední řadě bych pak chtěl poděkovat rodičům a slečně Monice Nerudové za podporu v průběhu celého studia.
Online plánování nejvýhodnější trasy s využitím open-source aplikací Abstrakt Cílem této práce je vytvoření webové mapové aplikace pro plánování nejvýhodnější trasy při využití open-source nástrojů. Na začátku práce jsou použité nástroje stručně představeny a následuje popis postupu tvorby samotné aplikace. Práce popisuje způsob získání, úpravy i uložení dat použitých pro hledání trasy. Popsán je i postup použitý při tvorbě samotné webové mapové aplikace. Pro uložení dat byl použit databázový systém PostgreSQL a jeho extenze PostGIS. Vyhledání trasy zajišťuje knihovna pgRouting a vizualizace výsedků nad mapou OpenStreetMap je realizována pomocí knihovny OpenLayers. Při tvorbě aplikace byly použity skriptovací jazyky PHP a Javascript. Klíčová slova: pgRouting, plánování trasy, PostGIS, Mapserver, OpenLayers
Online route planning with open-source applications Abstract The goal of this work is to create a route planning web application while using open source tools and applications. In the beginning, tools used to produce such application are briefly introduced. The the process of creating the application is described. This paper describes the proces sof getting, editing and storing data used for route planning. The proces sof creating the web mapping application itself is described, too. Data have been stored using PostgreSQL database system and its extension PostGIS. Route planning is realized using pgRouting library and the resulting route is displayed using OpenLayers library and OpenStreetMap as a base map. The application has been created using PHP and Javascript. Keywords: pgRouting, route planning, PostGIS, Mapserver, OpenLayers
Obsah Seznam obrázků a zdrojových kódů ................................................................... 9 1. Úvod .................................................................................................................10 1.1 Literární rešerše ..........................................................................................11 2. Teoretický rámec .............................................................................................14 2.1 Webové mapové aplikace ...........................................................................14 2.2 PostrgeSQL.................................................................................................16 2.3 PostGIS .......................................................................................................17 2.4 pgRouting ....................................................................................................18 2.5 Mapserver ...................................................................................................18 2.6 OpenLayers.................................................................................................19 3. Příprava softwaru ............................................................................................20 3.1 PostgreSQL.................................................................................................20 3.2 PostGIS .......................................................................................................21 3.3 pgRouting ....................................................................................................22 3.4 Mapserver ...................................................................................................23 3.5 OpenLayers.................................................................................................24 4. Zpracování zadané aplikace ...........................................................................25 4.1 Základní charakteristika aplikace ................................................................25 4.2 Výběr testovacího území .............................................................................26 4.3 Příprava databáze a dat ..............................................................................26 4.3.1 Úprava dat před nahráním do databáze ..............................................27 4.3.2 Nahrání dat do databáze .....................................................................28 4.3.3 Úprava dat v databázi ..........................................................................28 7
4.3.4 Nastavení ceny ....................................................................................31 4.4 Tvorba uživatelského prostředí aplikace .....................................................37 4.4.1 Skript OpenLayers ...............................................................................38 4.4.2 Formulář pro uzpůsobení vyhledávané trasy .......................................44 4.4.3 Hledání trasy ........................................................................................46 4.4.4 Mapfile .................................................................................................51 5. Výsledky ...........................................................................................................55 6. Diskuze a závěr ................................................................................................59 Seznam zdrojů informací ....................................................................................61 Seznam příloh ......................................................................................................64
8
SEZNAM OBRÁZKŮ A ZDROJOVÝCH KÓDŮ Obrázek 1
Schéma funkce mapového serveru
Obrázek 2
Vytvořená aplikace v prohlížeči Google Chrome
Obrázek 3
Trasa pro silniční kolo mezi obcemi Herálec a Křižánky
Obrázek 4
Trasa pro trekové kolo mezi obcemi Herálec a Křižánky
Obrázek 5
Trasa pro horské kolo mezi obcemi Herálec a Křižánky
Zdr. kód 1
Tvorba databáze s podporou PostGIS a pgRouting
Zdr. kód 2
Možný způsob vložení skriptu OpenLayers do zdrojového kódu
Zdr. kód 3
Použitý způsob vložení skriptu OpenLayers do zdrojového kódu
Zdr. kód 4
Poslední úpravy dat pro použití s knihovnou pgRouting
Zdr. kód 5
Nastavení sloupce length tabulky road_bike
Zdr. kód 6
Nastavení sloupce length tabulky trek_cross
Zdr. kód 7
Nastavení sloupce length tabulky mtb
Zdr. kód 8
Skript OpenLayers
Zdr. kód 9
Spuštění funkce init()
Zdr. kód 10
Vložení mapy s názvem map, vytvořené funkcí init() do webové stránky.
Zdr. kód 11
Formulář pro zadání údajů o požadované trase
Zdr. kód 12
Formulář pro zrušení zobrazení nalezené trasy.
Zdr. kód 13
Zpracování dat formuláře
Zdr. kód 14
Vyhledání požadované trasy
Zdr. kód 15
Ošetření některých možných chyb
Zdr. kód 16
Mapfile
Zdr. kód 17
„Magický řetězec“ 9
KAPITOLA 1 Úvod Snad každý, kdo má přístup k internetu a potřeboval jet někam, kde předtím nikdy nebyl, vyzkoušel některý z mnoha dostupných plánovačů tras. Využití těchto nástrojů může ušetřit spoustu času, který bychom jinak museli trávit hledáním nejvhodnější trasy v mapě nebo zajížďkou při volbě nevhodné trasy. Na webu jsou dostupné také aplikace, které podobně naplánují trasu i pro cyklistiku, většina jich však umí „jen“ omezit jízdu po silnicích první třídy a silnicích, na něž se na jízdním kole nesmí, jako jsou například dálnice. Cyklista, který chce jet výhradně po asfaltových cestách a chce se vyhnout cyklostezkám vedoucím po lesních a polních cestách nebo naopak cyklista, který holduje jízdě v terénu a po asfaltu nechce jet více než je nezbytně nutné, je tak odkázán na znalost terénu a plánování trasy „po staru“ s mapou v ruce. V rámci této práce se tedy pokusím vytvořit mapovou aplikaci, jenž bude umět pracovat s takovými parametry, které výslednou trasu co nejvíce přizpůsobí potřebám uživatele. Předkládaná práce má za cíl vytvořit webovou mapovou aplikaci, jejímž primárním účelem je hledání nejvhodnější trasy nad vrstvou vektorových dat, a to s využitím volně dostupných a open source nástrojů. Zároveň se tím pokusím demonstrovat možnosti open source nástrojů při tvorbě webových mapových aplikací. Následně mám v úmyslu celou aplikaci modifikovat tak, aby hledala nejvhodnější trasu nejen po silnicích, ale také po ostatních cestách zmapovaných v podkladových vektorových datech. Tato data je pak potřeba upravit tak, aby bylo možné tuto aplikaci použít k nalezení nejvhodnější trasy s preferencí určitého druhu a povrchu cesty, tedy trasu vhodnou pro různé druhy cyklistiky od čistě silniční až po terénní s preferencí úzkých neudržovaných pěšinek. Pro tyto účely bylo potřeba použití většího množství nástrojů, z nichž každý měl poměrně specifickou funkci, a zároveň se s těmito nástroji naučit pracovat. Použitá prostorová data jsou uložena v databázovém systému PostgreSQL, který však 10
práci s prostorovými daty standardně nepodporuje a pro tento účel k němu bylo potřeba nainstalovat extenzi PostGIS, jenž umožňuje do databáze ukládat data ve formátu ESRI Shapefile (dále jen shapefile), ale i v dalších formátech. Uložená prostorová data je následně potřeba vykreslit ve webovém prohlížeči. To umožní aplikace Mapserver. Aby nebyla mapa jen statickým obrázkem, ale bylo možné měnit zobrazenou oblast výřezu, přibližovat a oddalovat, je možné použít knihovnu OpenLayers, která jednoduchým způsobem udělá ze statické mapy mapu interaktivní. Posledním článkem v této soustavě je knihovna pgRouting, která umožňuje vyhledání nejvhodnější trasy nad vektorovými daty uloženými v databázi PostGIS. Jednotlivým nástrojům se budu věnovat podrobněji v dalších kapitolách. Způsob, jakým byla tato práce napsána, ze strany čtenáře předpokládá alespoň základní znalost jazyků HTML, Javascript a PHP a dotazovacího jazyka SQL. Podrobné vysvětlování zdrojového kódu by bylo nad rámec této aplikace. O základech práce s jazyky HTML a Javascript jsou dostupné informace například v Janovský ([2000?]) pro pochopení jazyka PHP lze použít například Gilmore (2005).
1.1 Literární rešerše Literatura pro potřeby této práce je poměrně špatně dostupná. Protože je nejdůležitějším prvkem předkládané práce její praktická část, byly použity výhradně zdroje informací, které v první řadě vysvětlují práci s jednotlivými nástroji použitými při zpracování této práce. Jedním z nich Mitchell (2005). Tato kniha popisuje tvorbu webových mapových aplikací s využitím open source nástrojů. Na úvod popisuje, co to vlastně webové mapové aplikace a open source nástroje jsou, základní datové formáty prostorových dat a způsoby práce s nimi od způsobu uložení, přes konverzi datových formátů až po editaci a tvorbu vlastních dat a způsoby jejich vizualizace ve webové mapové aplikaci. Dále se věnuje instalaci nástrojů potřebných pro vývoj a správnou funkci webových mapových aplikací. Přínos pro předkládanou práci tkví zejména v tom, že se podrobně věnuje i práci s Mapserverem, PostGISem a databází PostgreSQL. Na konci je pak věnována jedna kapitola tvorbě statických map a jedna kapitola tvorbě interaktivních map. Hlavní důvod, proč jsem čerpal formace z této knihy je, že se věnuje práci s několika nástroji, které používám ke zpracování praktické části předkládané práce. 11
Dále jsem využil knihu Billa Kroply (2005). Kniha se věnuje výhradně práci s Mapserverem. Začíná popisem instalace a pokračuje tvorbou statických a interaktivních mapových aplikací. Nejvíce prostoru je však věnováno programování pro Mapserver s využitím jazyků Python, Perl a PHP a na konci se nachází kapitola o rozšíření schopností Mapserveru s využitím databáze MySQL. Velká část knihy tedy obsahuje informace, které jsem v této práci nevyužil, nicméně informace v kapitolách věnujících se tvorbě webových mapových aplikací se samotným Mapserverem byly velmi užitečné. Informace o databázovém systému PostgreSQL jsem čerpal z Momjian (2003). V knize lze najít velmi podrobný popis práce s databázovým systémem PostgreSQL. Pro tuto práci byla využita zejména kvůli teoretickým informacím o PostgreSQL. Velmi důležité informace o Mapserveru jsem nalezl ve webové dokumentaci Mapserveru v Mapserver ([2010?]). Užitečný je zejména průvodce, který formou jednoduchých příkladů seznamuje s jednotlivými částmi mapového souboru, mapfilu (viz dále) a pro začátek práce s Mapserverem je daleko přehlednější než informace v dříve zmíněných knihách. Způsob práce s knihovnou pgRouting je velmi dobře popsán v Techer (2008). Oproti dokumentaci ke knihovně pgRouting má tento zdroj výhodu zejména v tom, že popisuje různé funkce této knihovny na praktických příkladech a podrobně vysvětluje každý krok, což je pro správné pochopení funkčnosti knihovny pgRouting klíčové. Využil jsem však i informace z oficiální dokumentace knihovny pgRouting. Po pochopení základních principů je tato dokumentace, dostupná v pgRouting Project ([2010?]d), užitečná zejména proto, že obsahuje více informací, i když ne tak pečlivě a podrobně popsaných jako v Techer (2008). Pro začátek práce s knihovnou OpenLayers byl užitečný návod na blogu Erika Hazzarda. Zmiňovaný návod má tři části, první z nich je dostupná na Hazzard (2009), přičemž odkaz na další část je vždy uveden na konci aktuálně zobrazené části. Nakonec jsem zjišťoval základní informace o algoritmech použitých pro hledání nejvýhodnější trasy v knihovně prRouting. Cílem této práce není hodnocení těchto algoritmů, ale přesto je vhodné o nich alespoň něco vědět. Základní informace o fungování algoritmů knihovny pgRouting jsou dostupné v prezentaci Antona Patrusheva „Shortest path search in real road network with pgRouting“, která byla 12
prezentována na konferenci FOSS4G (Free and Open Source Software for Geospatial) v roce 2007. Podrobnější popis fungování Dijkstrova algoritmu je dostupný v Hazzard (2010a). Ačkoliv kontextuálně zcela nesouvisí s knihovnou pgRouting, přesto lze na základě uvedených informací lépe pochopit, jak vlastně hledání nejkratší trasy funguje.
13
KAPITOLA 2 Teoretický rámec V této kapitole nejprve představím stručný úvod do problematiky webových mapových aplikací a poté věnuji jednu podkapitolu každému z nástrojů vyjmenovaných v kapitole 1, které umožňují správnou funkci zpracovávané aplikace. Každá podkapitola obsahuje stručnou charakteristiku daného nástroje a popis jeho vlastností. Zabývám se zde především těmi vlastnostmi a funkcemi, jenž jsou využity ve zpracovávané aplikaci, detailnější popis by již překračoval rámec této práce. Všechny tyto nástroje jsou volně šiřitelné na základě takzvané open source licence. Open source licence umožňuje bezplatné používání softwaru a tyto aplikace jsou primárně distribuovány ve formě zdrojového kódu, který si každý může modifikovat dle vlastních potřeb (Open Source Inititative [2010?]). V rámci této práce nebyly modifikace zdrojového kódu nutné, proto byly aplikace staženy již zkompilované, což umožnilo jejich instalaci okamžitě po stažení. Vytvářená aplikace staví na dvou základních pilířích, jejichž funkčnost pak rozšiřují další aplikace. Správná funkce těchto rozšiřujících aplikací je závislá na některém ze dvou základních prvků a celý systém je tak těsně provázaný, protože mezi sebou musí spolupracovat nejen základní aplikace a jejich rozšíření, ale také obě základní aplikace vzájemně.
2.1 Webové mapové aplikace Webové mapové aplikace jsou velmi efektivní způsob jak zajistit dostupnost určité informace pro široké spektrum uživatelů. Mapy, které webová mapová aplikace vytváří, lze rozdělit do dvou základních kategorií na statické a interaktivní (Mitchell, 2005). Mapu, ať už statickou nebo interaktivní, většinou vytváří mapový server. Obrázek 1 schematicky ukazuje, jak probíhá proces tvorby mapy pro webovou 14
mapovou aplikaci. Uživatel si spuštěním aplikace (otevřením webové stránky obsahující mapovou aplikaci) vyžádá od webového serveru mapu. Ten tuto žádost žád předá mapovému serveru, jenž si vyžádá data, která potřebuje pro vytvoření ytvoření mapy, z místa, kde jsou data uložena. Následně vytvoří mapu podle instrukcí, které má zadané od autora aplikace a hotovou mapu předá webovému serveru. Ten pak mapu zobrazí v počítači uživatele (Mitchell, 2005).
Obrázek 1: Schéma funkce mapového serveru (upraveno podle Mitchell, 2005)
Statická mapová aplikace má ve webovém prohlížeči podobu obrázku, se kterým uživatel nemůže nijak manipulovat, tedy nemůže měnit rozsah zobrazeného území, měřítko, ani zobrazené informace (vrstvy) (Mitchell, 2005). 2005) Statickou mapu lze tedy teoreticky vytvořit například i oskenováním mapy papírové – pokud bude sken kvalitní, uživatel jen těžko rozpozná rozdíl mezi statickou mapou vytvořenou mapovým serverem a statickou mapou vloženou na webovou stránku již jako hotový obrázek. Tvorba statické mapy je poměrně jednoduchá, stačí mapovému serveru zadat zadat, odkud má načíst data (rastrové obrázky, například satelitní snímky, nebo vektorová data) a jak má data vykreslit (barva a další parametry u vektorových dat, u rastrových dat například rozsah zobrazeného území). Na základě těchto instrukcí pak mapový server mapu vykreslí a ta se nakonec zobrazí v počítači uživatele. Interaktivní mapová aplikace má na monitoru koncového uživatele ve většině případů také podobu rastrového obrázk obrázku. u. Její součástí však jsou i další prvky, které umožňují například změnu měřítka nebo posouvání zobrazeného výřezu 15
mapy. Poměrně rozšířené jsou také mapové aplikace, jenž nechají uživatele vybrat z několika vrstev, které je možné zobrazit nad podkladovou mapou, případně změnit podkladovou mapu (Mitchell, 2005). Tyto samotné prvky však většinou „jen“ zvyšují komfort prohlížení a umožňují využití mapy pro zobrazení rozdílně velkých územních celků. Je proto časté, že taková mapová aplikace má nějaký jiný hlavní účel, než samotné prohlížení mapy (specifickou skupinou jsou mapy tematické, které umožňují přepínání mezi zobrazenými tematickými vrstvami, často statistického charakteru). Mezi běžně rozšířené funkce patří hledání nejvhodnější trasy mezi dvěma body. Navzdory rozšířenosti aplikací s podobným účelem se mi nepodařilo nalézt ani jednu mapovou aplikaci, která by umožňovala opravdu komplexní nastavení parametrů hledání, jak jsem již zmínil. To může být dáno tím, že většina takových aplikací je zaměřena na hledání trasy pro cestu automobilem a možnost vyhledání optimální trasy pro cestu na jízdním kole nabízí jen jako jakousi třešničku na dortu. Pro cestu automobilem opravdu stačí zvolit, jestli chce uživatel využívat placené úseky silnic a jestli hledá cestu nejkratší nebo nejrychlejší. Pro cestu na kole je však často třeba umožnit podrobnější nastavení parametrů, protože každý cyklista vyžaduje jiný druh trasy. Tvorba interaktivní mapy je o poznání náročnější, než tvorba mapy statické, proto se s nimi na webu také setkáváme méně (Mitchell, 2005). To se však poslední dobou postupně mění, jak různé mapové portály, například Google Maps, vyvíjejí možnosti, jak mohou uživatelé do jejich map vkládat omezené množství vlastních informací a takovou mapu pak například zveřejnit na svých webových stránkách (Maps Help, c2011). Tento způsob, jak jednoduše vložit na své webové stránky interaktivní mapu, je velmi intuitivní, nicméně možnosti využití takové mapy jsou značně omezeny tím, co umožní poskytovatel této služby. Pokud chceme mapovou aplikaci s vlastními daty, vlastní symbologií a vlastní funkčností, musíme ji vytvořit sami.
2.2 PostrgeSQL Databázový systém PostgreSQL je prvním ze základních pilířů vytvářené mapové aplikace. Funguje jako úložiště prostorových dat. Právě tento systém byl zvolen díky dvěma extenzím, které jsou pro zpracování této práce klíčové, a to PostGIS (viz kapitola 2.3) a pgRouting (viz kapitola 2.4). 16
PostgreSQL je populární především pro svoji spolehlivost a bezpečnost (Momjian, 2003). Má i řadu dalších výhod, jako kvalitně zpracovanou dokumentaci a vstřícnou komunitu uživatelů, kteří v případě problému ochotně poradí (Momjian, 2003), tyto výhody však při výběru databázového systému nehrály příliš velkou roli; jak je zmíněno dříve, klíčová byla dostupnost extenzí vhodných pro zpracování zadané aplikace. Vývoj PostgreSQL započal již v 70. letech 20. století na univerzitě v Berkeley v Kalifornii pod jménem Ingres. V letech 1994-1995 byl doplněn o podporu jazyka SQL a přejmenován na Postgres95, a zároveň jeho vývoj opustil univerzitu v Berkeley. V roce 1996 se původně univerzitní projekt začal transformovat na Open source projekt a na vývoji se postupně začali podílet další lidé z celého světa a byl definitivně přejmenován na PostgreSQL (Momjian, 2003). Jak jsem již popsal (a zároveň vyplývá z názvu), PostgreSQL podporuje primárně databázový dotazovací jazyk SQL (Standard Query Languge, standardní dotazovací jazyk). Pro potřeby této práce stačí pouze elementární znalosti jazyka SQL, protože většinu dotazů si vytvoří použité aplikace samy.
2.3 PostGIS PostGIS je extenze databázového systému PostgreSQL, jenž mimo jiné umožní do databází ukládat prostorová data v několika formátech (Mitchell, 2005). Zároveň s prostorovými daty uloží i k nim přiřazená atributová data ve formátu .dbf, pokud jsou k dispozici. Uložená prostorová data pak v databázovém systému PostgreSQL vystupují jako tabulka a lze s nimi také tak nakládat. Nejedná se však pouze o extenzi sloužící k ukládání prostorových dat. S pomocí PostGIS získáme schopnost s daty jednoduše manipulovat pomocí příkazů jazyka SQL a lze tak provádět jednoduché analýzy bez nutnosti použití dalších nástrojů. Jako příklad takovéto jednoduché analýzy lze uvést výběr bodů, které se nacházejí uvnitř určitého polygonu (Mitchell, 2005). Mezi složitější operace, jež umožňuje PostGIS provádět s daty uloženými v databázi PostgreSQL, patří například změna souřadnicového systému dat (Mitchell, 2005). Pro potřeby této práce je obsluha PostGIS velmi jednoduchá, v podstatě jde jen o nahrání dat ve formátu shapefile do databáze. To lze provést buď pomocí SQL dotazu nebo pomocí aplikace PostGIS Shapefile and DBF Loader, jenž je
17
dostupný od verze PostGISu 1.5, která v době psaní této práce byla zároveň verzí nejnovější.
2.4 pgRouting pgRouting je rozšiřující knihovna pro PostGIS, která po nahrání do databáze PostGIS (jenž je vlastně databází PostgreSQL rozšířenou o podporu prostorových dat) umožní nad vektorovými vrstvami v této databázi provádět síťovou analýzu. Konkrétně umožňuje nalezení nejkratší trasy podle jednoho ze tří algoritmů, výpočet dojezdové vzdálenosti a řešení úlohy obchodního cestujícího (pgRouting Project [2010?]b). Pro potřeby této práce je důležitá zejména možnost nalezení nejkratší trasy. Knihovna pgRouting vyhledává nejkratší trasu podle jednoho ze tří zvolených algoritmů. Prvním z nich je Dijkstrův algoritmus, velmi známý a uznávaný algoritmus, který vždy najde matematicky nejkratší cestu (Patrushev, 2007). Dále je možné použít algoritmus A-star (také známý jako A*), který hledá nejkratší trasu na základě metody pokus-omyl (Patushev, 2007). Poslední z dostupných algoritmů je Shooting-star (také známý jako Shooting*), provádějící analýzu po spojnicích, nikoliv uzlech jako předchozí dva algoritmy (Patrushev, 2007). Nejkratší trasu pgRouting nalezne podle „ceny“ nastavené individuálně pro každý úsek cesty, přičemž hledá cestu s nejnižší „cenou“. Jako cena se běžně používá vzdálenost nebo čas.
2.5 Mapserver Mapserver vytváří rastrový obrázek mapy na základě prostorové informace uložené v digitálním formátu a umí pracovat s vektorovými i rastrovými daty ve velkém počtu formátů (Mitchell, 2005). Jakým způsobem vykreslit mapu říká Mapserveru takzvaný mapfile, což je něco jako šablona, která řekne jaké prvky z jaké vrstvy vykreslit a jakou symbologii k tomu použít (Mitchell, 2005). Samotný mapserver není analytický nástroj, nýbrž s pomocí různých kartografických prostředků pouze vizualizuje výsledky analýzy, kterou provedly jiné nástroje. Při použití samotného mapfilu Mapserver vykreslí pouze rastrový obrázek mapy. Pro vytvoření interaktivní mapy, kterou bude možné přibližovat a oddalovat 18
nebo posouvat její zobrazený výřez, je potřeba použít mapfile společně s takzvaným HTML template souborem. HTML template je soubor, jenž se skládá ze standardních HTML značek (jako běžná webová stránka) a ze zástupných zkratek Mapserveru. Tyto zkratky například zastupují cestu k rastrovému obrázku, ve kterém je uložena aktuálně zobrazená mapa a Mapserver je vždy nahradí hodnotou, s níž v této proměnné zrovna pracuje (Mitchell, 2005). S využitím pokročilých postupů lze pomocí Mapserveru vytvořit velmi komplexní aplikace. Jak již bylo zmíněno, bez použití dalších nástrojů bude mapová aplikace sloužit pouze k prohlížení dat různými způsoby (statická/interaktivní mapa), zatímco další nástroje, jako například PostGIS nebo pgRouting psoužité v této práci, mohou Mapserveru dodat funkcionalitu srovnatelnou s komerčními GIS produkty (Mitchell, 2005). Vhodně vytvořená webová mapová aplikace pak může umožnit provádění analýz i uživatelům, kteří jinak s geoinformačními systémy pracovat neumějí.
2.6 OpenLayers OpenLayers je volně dostupný skript pro webové stránky. Usnadňuje vytvoření interaktivní mapy tak, že autorovi aplikace stačí do webové stránky napsat několik řádků v jazyce JavaScript a vložit odkaz na skript OpenLayers (Hazzard, 2009). Tvorba interaktivní mapy tak nevyžaduje speciální dovednosti a stačí umět vytvořit mapový soubor pro statickou mapu. Skript OpenLayers pak tuto statickou mapu načte jako vrstvu přes službu WMS a dodá ji požadovanou funkčnost (většinou možnost změny měřítka a posouvání výřezu). Další výhodou tohoto skriptu je, že umožňuje velmi jednoduchým způsobem do mapové aplikace vložit podkladovou mapu, například OpenStreetMap (Hazzard, 2009), což je vlastnost, která při tvorbě této práce byla velmi užitečná.
19
KAPITOLA 3 Příprava softwaru Před začátkem tvorby samotné webové mapové aplikace je potřeba nainstalovat software, umožňující její zpracování a běh. V této kapitole popíšu, jaký software a z jakých zdrojů jsem instaloval.
3.1 PostgreSQL Instalační soubor databázového systému PostgreSQL pro systémy Windows je možné stáhnout na adrese http://www.postgresql.org/download/windows. Pro tuto práci jsem použil „One click installer“, který je nejjednodušším způsobem jak PostgreSQL nainstalovat. Instalace probíhá podobně jako u většiny programů a je velmi rychlá a jednoduchá. Instalátor zajímalo pouze to, kam by se měl systém nainstalovat (zde jsem ponechal původní možnost, tedy c:\Program Files (x86)\PostgreSQL\). Dále pak uživatelské jméno, které jsem také ponechal na přednastavené hodnotě „postgres“, heslo, jež jsem nastavil na „92670“ (Heslo je krátké a obsahuje pouze čísla, což nemusí být úplně bezpečné. Pro účely spouštění aplikace na počítači, kdy aplikace není veřejně dostupná, to však postačuje.) a port, jemuž jsem ponechal původní hodnotu „5432“. Tyto údaje jsou důležité pro připojení k databázi. Nastavení těchto čtyř parametrů (z nichž tři je možné ponechat na původní hodnotě) je tedy jediný aktivní zásah uživatel, který instalace PostgreSQL vyžaduje. Tedy mimo klikání na tlačítko s nápisem „Další“, jenž posune instalaci o krok vpřed. Po instalaci není nutné provádět žádné další akce a je možné databázový systém rovnou začít používat. V případě této práce však bylo nutné nainstalovat ještě rozšíření PostGIS a dále knihovnu pgRouting, což bude popsáno v dalších podkapitolách.
20
Systém PostgreSQL jsem nainstaloval ve verzi 8.4, přestože v době psaní této práce již byla vydána verze 9. Důvodem je, že jsem pracoval s knihovnou pgRouting ve verzi 1.03 (21.1.2011 nejnovější verze dostupná jako zkompilovaná pro Windows) a ta nebyla kompatibilní s nejnovější verzí PostgreSQL. Jednou z příjemných aplikací, které se nainstalují spolu s databázovým systémem PostgreSQL, je pgAdmin. Tato aplikace je dostupná od verze PostgreSQL 7.3 (PgAdmin: PostgreSQL administration and management tools [2010?]) a umožňuje kompletní možnosti správy systému PostgreSQL. Díky aplikaci pgAdmin tedy není nutné k databázím přistupovat přes příkazový řádek, ale je k dispozici přehledné prostředí, v němž lze snadno provádět veškeré úkony související se správou databází. Prostředí této aplikace je uspořádáno účelně a ovládání je intuitivní, ke správě databází v něm tedy není třeba žádných zvláštních znalostí. Díky možnosti grafického sestavování databázových dotazů by nebylo potřeba ani ovládat jazyk SQL. Osobně však preferuji sestavení dotazů pomocí jazyka SQL, díky jeho relativní jednoduchosti (navíc ani dotazy použité při zpracování této práce nebyly nijak komplexní, většinou pouze na jeden řádek) se mi to jeví jako přehlednější varianta.
3.2 PostGIS Společně se systémem PostgreSQL se nainstalovala i aplikace Application Stack Builder, jejíž spuštění je uživateli nabídnuto ihned po dokončení instalace systému PostgreSQL. Proč tuto aplikaci zmiňuji v kapitole, která by se měla věnovat instalaci PostGISu? Application Stack Builder je totiž nejjednodušší způsob jak do již nainstalovaného systému PostgreSQL přidat extenzi PostGIS. Po spuštění Application Stack Builder se aplikace zeptá, se kterou instalací systému PostgreSQL chceme pracovat. Zde stačí vybrat požadovanou verzi PostgreSQL (v mém případě byla k dispozici pouze jedna, jelikož jsem nainstaloval pouze jednu). V dalším kroku se z internetu stáhne seznam dostupných extenzí, a je potřeba vybrat, kterou má aplikace nainstalovat. Ve složce Spatial Extensions je k dispozici PostGIS ve verzi 1.4.2 a 1.5. Vybral jsem verzi 1.5, která na rozdíl od verze 1.4.2 obsahuje i aplikaci pro jednoduché nahrávání prostorových dat do databáze, což se jinak musí řešit pomocí SQL dotazů nebo příkazového řádku. V dalším kroku je požadován výběr serveru, z něhož se má vybraná extenze stáhnout. Následně se extenze stáhne a proběhne instalace (tu je možné přeskočit 21
a nainstalovat stažené extenze později). Instalační program se už jen zeptá, zda má vytvořit šablonu databáze s podporou PostGIS a následně je požadováno uživatelské jméno, heslo a port pro připojení k databázi. Instalace extenze PostGIS je tedy také jednoduchá, stejně jako následná tvorba databází s podporou PostGIS kdy stačí jako šablonu vybrat databázi, která byla vytvořena při instalaci. Pokud nebyla tato šablona vytvořena, je nutné do nově vytvořené databáze podporu PostGIS přidat pomocí SQL dotazů, což je podrobně popsáno v dokumentaci PostGIS a v rámci této práce nebylo použito.
3.3 pgRouting Instalace knihovny pgRouting už je o trochu složitější než v případě systému PostgreSQL resp. jeho extenze PostGIS. Knihovnu jsem stáhnul z adresy http://www.pgrouting.org/download.html. Konkrétně jsem stahoval soubor pgRouting-1.03_pg-8.4.2.zip, který se na stránce nachází pod nadpisem Windows Binaries. Funkcionalitu knihovny pgRouting je nutné přidat do každé databáze zvlášť. V průběhu testování aplikace jsem používal dva různé způsoby pro přidání funkcí knihovny pgRouting do databází, uvedu zde proto oba. Postup popisující instalaci knihovny pgRouting do databáze PostgreSQL a uvedený v tomto odstavci vychází z Techera (2008), některé drobnosti však byly pro potřeby této práce upraveny. Stažený archiv jsem rozbalil a adresáře share a lib zkopíroval do složky, kde je nainstalovaný systém PostgreSQL, v mém případě c:\Program Files (x86)\PostgreSQL\8.4\. Dále jsem přidal cesty c:\Program Files (x86)\PostgreSQL\8.4\bin a c:\Program Files (x86)\PostgreSQL\8.4\lib do proměnné prostředí Path. V dalším kroku jsem otevřel příkazový řádek a spustil příkazy uvedené ve zdrojovém kódu 1. První řádek určuje jméno uživatele databázového systému PostgreSQL. Druhý řádek znamená, že všechny soubory se budou hledat v zadaném adresáři. Na třetím řádku je vytvořena databáze s názvem testgis podle šablony s názvem template_postgis (šablony vytvořené při instalaci PostGIS). Další dva řádky pak v této databázi provedou v nově vytvořené databázi sérii SQL dotazů uvedenou v souborech routing_core.sql a routing_core_wrappers.sql. Po provedení těchto kroků již databáze podporuje funkcionalitu knihovny pgrouting, i když ne kompletní (v použitých souborech chybí podpora problému obchodního cestujícího, což však na zpracování zadané aplikace nemá vliv) a je možné ji začít plně využívat. 22
SET PGUSER=postgres cd C:\Program Files (x86)\PostgreSQL\8.4\share\contrib createdb -T template_postgis testgis psql -d testgis -f routing_core.sql psql -d testgis -f routing_core_wrappers.sql Zdrojový kód 1: Tvorba databáze s podporou PostGIS a pgRouting (upraveno podle Techer (2008))
Druhou možností je realizovat podobný postup pomocí aplikace pgAdmin. Stejně jako v předchozí variantě jsem rozbalil složky lib a share ze staženého archivu do složky C:\Program Files (x86)\PostgreSQL\8.4\. Tentokrát však na adresáři, do kterého byl archiv rozbalen, nezáleží. Pak jsem v programu pgAdmin vytvořil novou databázi podle šablony template_postgis, což je šablona databáze s podporou PostGIS vytvořená při instalaci této extenze. Po vytvoření databáze jsem otevřel okno pro provádění SQL dotazů a v tomto okně jsem otevřel nejprve soubor routing_core.sql nacházející se v adresáři C:\Program Files (x86)\PostgreSQL\8.4\share\contrib, což je adresář, do něhož jsem dříve rozbalil archiv s knihovnou pgRouting. Pak jsem označil celý text a nechal provést dotazy. Stejný postup jsem pak uplatnil u souboru routing_core_wrappers.sql. Databáze vytvořená jedním z uvedených způsobů již podporuje základní dotazy na vyhledání nejvýhodnější trasy a je možné ji použít buď jako šablonu pro snadnou a rychlou tvorbu dalších takových databází nebo ji začít využívat pro ukládání prostorových dat, nad nimiž bude možné provádět sítovou analýzu.
3.4 Mapserver Mapserver jsem stáhnul jako součást balíku aplikací ms4w z http://maptools.org/ms4w/index.phtml?page=downloads.html. Tento balík aplikací obsahuje nejen Mapserver, ale také webový server Apache, skriptovací jazyk PHP a další aplikace, které při práci s Mapserverem mohou být potřeba. Instalace probíhá velmi jednoduše, uživatel je pouze požádán, aby vybral, jaké součásti balíku si přeje nainstalovat. Instalátor pak požadované aplikace stáhne a nainstaluje. Od uživatele nejsou vyžadovány žádné další operace. Jestli se Mapserver a ostatní komponenty skutečně správně nainstalovaly, jsem ověřil zadáním adresy http://localhost do webového prohlížeče. Načtení uvítací stránky MS4W znamená, že vše proběhlo v pořádku a je možné začít využívat Mapserver.
23
3.5 OpenLayers Používání skriptu OpenLayers je velmi jednoduché. Existují zde dvě možnosti. První z nich je vložit do zdrojového kódu stránky, kde chceme OpenLayers používat, ideálně do hlavičky, řádek s kódem zobrazený ve zdrojovém kódu 2. Poté už může následovat skript psaný v jazyku javascript, kterým bude vytvořena dynamická mapa. Zdrojový kód 2 odkazuje na skript v jazyce javascript, jenž je uložený na adrese uvedené v atributu src. <script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"> Zdrojový kód 2: Možný způsob vložení skriptu Openlayers do zdrojového kódu. (Hazzard, 2009)
Další variantou je tento skript stáhnout z adresy uvedené ve zdrojovém kódu 2 v atributu src a uložit jej na pevný disk počítače, na kterém aplikace poběží. Důležité je uložit jej do složky, do níž má webový server Apache přístup. Absolutní nebo relativní cestu k tomuto skriptu pak stačí uvést v atributu src. <script src="Scripts/OpenLayers/OpenLayers.js"> Zdrojový kód 3: Použitý způsob vložení skriptu OpenLayers do zdrojového kódu.
Pro potřeby této práce byla použita druhá varianta, tedy stažení skriptu na pevný disk a odkázání na něj v hlavičce stránky s aplikací. Použitý kód je ve zdrojovém kódu 3.
24
KAPITOLA 4 Zpracování zadané aplikace 4.1 Základní charakteristika aplikace Aplikace vytvořená v rámci této bakalářské práce se sestává z jedné webové stránky, obsahující skripty psané v jazyce Javascript a PHP, jednoho mapfilu, jednoho externího skriptu a databáze s daty. V databázi, vytvořené podle postupu uvedeném v kapitolách 3.2 a 3.3, je uloženo více vrstev obsahujících prostorová data. Celkem obsahuje tři liniové vrstvy a jednu bodovou vrstvu. Liniové vrstvy obsahují stejná geometrická data, liší se však v datech atributových. Ta jsou u každé vrstvy uzpůsobena jinému typu vyhledávané trasy, liší se tedy sloupce udávající „cenu“ za projetí určitého úseku. Data liniových vrstev byla získána z dat OpenStreetMap oříznutím podle hranic testovacího území. V bodové vrstvě jsou zakresleny uzly nacházející se mezi jednotlivými liniemi. Důležitá jsou u ní atributová data, ve kterých byl vybraným bodům přiřazen textový název. Podle tohoto názvu se zjistí identifikační číslo bodu a to je pak použito v dotazu na nejvýhodnější trasu. Webová stránka slouží jako uživatelské prostředí aplikace a zobrazuje pomocí skriptu OpenLayers podkladovou mapu. Jako podkladová mapa slouží mapa OpenStreetMap, jenž je načtena přes službu WMS. Mapa zabírá většinu obrazovky, jen v její pravé části se nachází formulář, do kterého uživatel zadá údaje požadované pro vyhledání trasy. V případě, že uživatel formulář odešle s platnými údaji, se provede vyhledání požadované trasy a její zobrazení na podkladové mapě. Vrstva s vyhledanou trasou je v mapě zobrazena pomocí Mapserveru a údaje o způsobu, jakým má být zobrazena (například barva), jsou uvedeny v mapfilu.
25
4.2 Výběr testovacího území Nejprve bylo třeba vybrat vhodné území, na kterém bych mohl testovat hledání různých typů tras (tedy tras vhodných pro rozdílné druhy cyklistiky). Vybrané území má na východním okraji obec Ždírec nad Doubravou, severní okraj je v nejsevernějším místě úseku silnice I/34 mezi Hlinskem a Poličkou. Východní okraj je u města Polička (město není v území zahrnuto) a jižní okraj se nachází na jižním okraji města Žďár nad Sázavou. Při výběru území hrálo roli více faktorů. Jako první zmíním kvalitu zpracování dat OpenStreetMap. Je totiž důležité, aby na vybraném území byly v datech OpenStreetMap zakresleny i nezpevněné cesty a pěšinky. Zároveň je vhodné, když jsou tyto menší cesty rovnoměrně rozmístěny po celé ploše území, což souvisí nejen s podrobností zpracování dat OpenStreetMap, ale také s charakterem krajiny v daném území. Další důležitou vlastností území je i přítomnost silnic, na kterých se cyklistika provozovat nesmí, případně to je z bezpečnostních důvodů nevhodné. Mezi takové silnice patří mimo jiné i silnice první třídy, od kterých cyklisté často očekávají hustý provoz automobilů, tak se jim raději vyhnou. Takových silnic je na vybraném území více, například již zmíněná I/34 v úseku mezi Ždírcem nad Doubravou a Poličkou. V neposlední řadě pak hrála roli i dostupnost území pro autora této práce. Hlinsko je mému bydlišti přibližně hodinu vzdálené. Dostupnost byla důležitá kvůli terénnímu průzkumu. Od původně plánovaného detailního průzkumu celého území, projetí nebo projití každého v datech OpenStreetMap zakresleného úseku cesty a následného individuálního přiřazení „ceny“ za projetí tohoto úseku jsem nakonec z časových důvodů upustil. Kvůli rozhodnutí o nastavení „ceny“ za projetí jednotlivých typů cest však bylo vhodné alespoň zjistit, jak ve skutečnosti vypadají cesty zařazené do různých typů rozlišovaných v datech OpenStreetMap.
4.3 Příprava databáze a dat Prvním krokem samotné tvorby aplikace bylo vytvoření databáze PostgreSQL, která bude obsahovat data, nad nimiž bude knihovna pgRouting hledat trasy. Použitím postupu popsaného v kapitolách 3.2 a 3.3 jsem takovou databázi vytvořil a kvůli přehlednosti (během testování různých postupů jsem již několik databází vytvořil) ji pojmenoval Hlinsko. 26
4.3.1 Úprava dat před nahráním do databáze Dále bylo potřeba získat data a upravit je do podoby, ve které by mohly fungovat ve vytvářené aplikaci. Data OpenStreetMap jsem stáhnul ze stránky http://download.geofabrik.de/osm/europe/, konkrétně jsem stahoval soubor czech_republic.shp.zip. Ze souboru jsem extrahoval shapefile silnic. Tento shapefile jsem pak v programu QGIS oříznul tak, aby obsahoval vybrané území. Obdélník, podle něhož jsem shapefile ořezával, jsem záměrně vytvořil větší, než bylo vybrané území. Důvodem bylo zachování možnosti mírně hranice testovacího území posouvat bez nutnosti znovu absolvovat celý proces přípravy dat. Po vytvoření výřezu dat bylo nutné data ještě upravit tak, aby se okraje liniových prvků nacházely na křížení jednotlivých linií. Okraje se mohou nacházet i na dalších místech, ale pokud nejsou na křížení linií, tak vyhledávání trasy knihovnou pgRouting nefunguje správně. Jak je uvedeno v pgRouting Project ([2010?]c), je potřeba, aby úsek mezi křížením cest byl tvořen jednou linií, tedy aby v místě křížení vždy byl začátek, respektive konec liniového prvku. Experimentováním jsem pak ověřil, že mezi kříženími cest se může nacházet libovolný počet liniových prvků, důležité je jen zachovat pravidlo o ukončení liniového prvku v místě křížení, jak již bylo uvedeno. Bohužel stažená data OpenStreetMap ve formátu .shp toto pravidlo nesplňují, což jsem ověřil v praxi a později i přečetl v pgRouting Project ([2010?]c). Řešení nabízené v pgRouting Project ([2010?]c) je stáhnout data OpenStreetMap v jiném formátu a následně stáhnnout nástroj OSMosis, který umožní extrahovat prostorová data do formátu, z nějž je pak lze pomocí dalšího nástroje převést do formátu, se kterým bude knihovna pgRouting umět pracovat. Bohužel se mi však nepodařilo nástroj OSMosis stáhnout, z uvedeného odkazu běželo stahování velmi pomalu a po chvíli se vždy přerušilo. Řešením bylo upravit data ve formátu .shp do potřebné podoby jinak. V tomto jediném kroku bylo nutné použít komerční software, protože potřebný nástroj jsem u žádného z volně dostupných produktů nenalezl. Data jsem otevřel v programu ArcMap 10 a spustil jsem nástroj Split Line At Vertices. Tato funkce rozdělí liniové prvky u každého uzlu, což je sice víc než bylo potřeba, ale účel, tedy rozdělení prvků v místě křížení linií, byl splněn. Nyní už bylo potřeba data jen převést do kartografického zobrazení používaného v mapě OpenStreetMap. Mapa OpenStreetMap, stejně jako například Google Maps, používá modifikované Mercatorovo zobrazení (OpenLayers, [2010?]b). Toto zobrazení je často označováno jako Google Mercator, Web 27
Mercator, nebo Spherical Mercator a v geoinformatice bývá označováno číselným kódem EPSG: 900913 (OpenLayers, [2010?]b). Převod do zobrazení EPSG: 900913 jsem provedl v aplikaci QGIS. Tato operace sice nebyla nezbytně nutná, protože Mapserver by byl schopen provádět převod do požadovaného zobrazení za běhu (což jsem zjistil experimentováním při tvorbě aplikace), ale v případě, že je Mapserver nucen provádět konverzi mezi kartografickými zobrazeními, trvá vykreslení mapy o poznání déle, proto jsem se rozhodl pracovat s daty již převedenými do potřebného kartografického zobrazení.
4.3.2 Nahrání dat do databáze Data připravená podle postupu v kapitole 4.3.1 jsem pak pomocí aplikace PostGIS Shapefile and DBF Loader nahrál do databáze Hlinsko. Při nahrávání do databáze jsem byl programem dotázán na přístupové údaje k databázi. Dále zde byla možnost zadat kódování atributových dat. Protože data obsahují diakritiku, je potřeba zde zadat LATIN1 (přestože se jedná o kódování pro západoevropské jazyky (PostgreSQL: The world's most advanced open source diabase, c2005) s diakritikou zde problém nebyl), případně by mělo být možné použít LATIN2 nebo WIN1250, což je kódování určené pro středoevropské jazyky (PostgreSQL: The world's most advanced open source diabase, c2005), to jsem ale v praxi nezkoušel. Pokud však v políčku pro nastavení kódování ponecháme původní hodnotu UTF8, aplikace ohlásí chybu a data do databáze nenahraje.
4.3.3 Úprava dat v databázi Nyní je vrstva cest nacházejících se na testovacím území nahrána v databázi Hlinsko. Tuto vrstvu jsem pojmenoval roads_split (pojmenoval jsem ji tak jako výstup dříve zmíněného nástroje Split Line At Vertices programu ArcMap 10). V databázi Hlinsko jsou již nahrané funkce knihovny pgRouting (viz kapitola 3.3), ještě je však na práci s těmito funkcemi potřeba připravit data. V první řadě je nutné přidat do tabulky sloupce length, cost a reverse_cost. Všechny sloupce budou datového typu float (desetinné číslo). Do sloupce length bude poté uložena délka liniového prvku a do sloupců cost a reverse_cost cena za projetí linie v obou směrech (jež bude zpočátku shodná s délkou prvku). Poté je potřeba vytvořit další dva sloupce s názvy source a target, které budou obsahovat celočíselnou hodnotu. Nakonec jsem spustil funkci assign_vertex_id(). Tato funkce přiřadí do sloupců 28
source a target celočíselný identifikátor okrajových uzlů každého liniového prvku. Jelikož tato funkce přiřazuje identifikátor zdrojového a cílového uzlu (source = anglicky zdroj, target = anglicky cíl) každému liniovému prvku, uloží tím do atributové tabulky zároveň informaci o směru vektoru liniového prvku. Současně s přiřazením identifikátorů uzlů vytvoří funkce assign_vertex_id() bodovou vrstvu nazvanou vertices_tmp obsahující všechny uzly a do atributové tabulky uloží těmto bodům jejich identifikátory. Když pak bude pgRouting hledat trasu, bude vždy požadovat celočíselné identifikátory uzlů, mezi kterými trasu hledá. Realizace postupu zmiňovaného v tomto odstavci pomocí SQL dotazů je uvedena ve zdrojovém kódu 4. První tři řádky vloží do tabulky roads_split sloupce s názvy length, cost a reverse_cost, jenž sdílejí datový typ float. Na čtvrtém řádku je do sloupce length vložena délka jednotlivých liniových prvků pomocí funkce ST_Length(). Funkce ST_Length() vrátí délku jednotlivých prvků v jednotkách prostorového zobrazení, v němž jsou data uložena (v případě použitého kartografického zobrazení tedy v metrech) a jako argument požaduje název sloupce s geometrií dat, který je při importování prostorových dat do databáze PostGIS přednastaven na the_geom (Refractions Research, [2010?]). Na následujících dvou řádcích je délka prvků vložena i do sloupců cost a reverse_cost. Nakonec jsou v tabulce roads_split vytvořeny sloupce source a target a je spuštěna funkce assign_vertex_id(). Funkce assign_vertex_id() požaduje čtyři argumenty. První je název vrstvy (která v databázovém systému PostgreSQL vystupuje jako tabulka, jak bylo zmíněno v kapitole 2.3). Dále požaduje vzdálenost, do které bude považovat dva sousední uzly za tentýž uzel (vzdálenost je v jednotkách použitého kartografického zobrazení). Poslední dva argumenty udávají název sloupce s geometrií a název sloupce s identifikačním číslem prvku. Název sloupce s geometrií lze při nahrávání dat do databáze měnit, pro účely této práce jsem však vždy ponechal přednastavený název the_geom.
29
ALTER TABLE roads_split ADD COLUMN length float; ALTER TABLE roads_split ADD COLUMN cost float; ALTER TABLE roads_split ADD COLUMN reverse_cost float; UPDATE roads_split SET length = ST_Length(the_geom); UPDATE roads_split SET cost = length; UPDATE roads_split SET reverse_cost = length; ALTER TABLE roads_split ADD COLUMN source integer; ALTER TABLE roads_split ADD COLUMN target integer; SELECT assign_vertex_id('roads_split', 0.00001, 'the_geom', 'gid'); Zdrojový kód 4: Poslední úpravy dat pro použití s knihovnou pgRouting. (upraveno podle pgRouting Project, [2010?]a)
V tuto chvíli by bylo možné začít používat vrstvu roads_split pro hledání tras pomocí knihovny pgRouting. Protože jako cena je nastavena délka linie, byla by vždy nalezena nejkratší trasa bez ohledu na typ cesty. Cílem zpracovávané aplikace však je, aby mohla hledat rozdílné typy tras s preferencí různých typů cest. Navíc, jak bylo zmíněno v kapitole 4.1, jsem se rozhodl problém, jak hledat různé typy tras, vyřešit použitím tří vrstev s rozdílným nastavením cen. Pro takové řešení jsem se rozhodl proto, že například oproti upravování cen v jedné tabulce za běhu aplikace se mi použití tří rozdílných vrstev jevilo jako méně kostrbaté. Navíc vzhledem k rozloze testovacího území není zvýšení nároků na úložný prostor, jakožto důsledek uložení většího množství dat, nijak výrazné. Abych nemusel každou z těchto vrstev připravovat poněkud zdlouhavým již zmiňovaným způsobem, použil jsem vrstvu roads_split, která již upravená je. Tuto vrstvu jsem v programu QGIS vyexportoval do formátu .shp a pomocí aplikace PostGIS Shapefile and DBF Loader ji se změněným názvem nahrál do databáze Hlinsko. Celkem jsem takto do databáze Hlinsko nahrál tři tabulky s názvy road_bike (pro hledání trasy pro silniční kolo), trek_cross (pro hledání trasy pro trekové resp. crossové kolo) a mtb (pro hledání trasy pro horské kolo), z nichž každá bude později upravena pro hledání různých typů tras. Časová úspora při použití takového postupu byla velmi výrazná, v tomto odstavci se pokusím vysvětlit proč. Jak jsem již zmínil, ve všech funkcích pro hledání trasy knihovna pgRouting požaduje jako vstupní parametry mimo jiné číselné identifikátory bodů, mezi kterými má trasu hledat. Uživatel aplikace však bude chtít do formuláře zadávat název bodu, například obce, tak, jak jej zná a jak jej vidí na mapě. Bylo tedy nutné aplikaci umožnit podle zadaného názvu bodu zjistit jeho identifikační číslo. K tomu poslouží vrstva vertices_tmp, která má u 30
každého bodu uložený jeho identifikátor. Body z vrstvy vertices_tmp jsem tedy zobrazil v aplikaci QGIS nad vrstvou roads_split, do atributové tabulky vrstvy vertices_tmp jsem přidal sloupec name a do tohoto sloupce jsem pak vepisoval názvy vybraných bodů. Pro účely této práce jsem vybíral a pojmenovával body reprezentující obce, nacházející se v testovacím území. Pojmenovával jsem všechny obce a konkrétní bod jsem vybíral porovnáním s mapou OpenStreetMap nebo Google Maps a výběrem bodu co nejblíže centru obce. Při rozhodování o poloze centra obce hrálo roli více faktorů, v některých případech to byla znalost obce, jindy blízkost obecního úřadu a v některých obcích jsem bod umístil třeba poblíž autobusové zastávky. Pokud se v pojmenovávané obci nenacházel význačný objekt, podle kterého bych určil polohu centra, pojmenoval jsem bod nacházející se na největší silnici (silnici nejvyšší třídy) procházející obcí. Tento postup byl velmi časově náročný. Při spuštění funkce assign_vertex_id() se však přiřazují identifikační čísla uzlů tak, že při každém spuštění funkce má stejný uzel jiné identifikační číslo. Pokud bych se tedy rozhodl použít tři různé vrstvy a každou připravit zvlášť, musel bych také pracovat se třemi tabulkami vertices_tmp (samozřejmě by každá musela mít jiné jméno, jinak by nemohly být ve stejné databázi, například vertices_tmp1, vertices_tmp2 atd.). Což by znamenalo trojí pojmenovávání bodů: protože identifikační číslo uzlu v jedné vrstvě by nebylo shodné s identifikačním číslem stejného uzlu v druhé vrstvě, bylo by nutné vytvořit tři tabulky s pojmenovanými body. Při použití dříve uvedeného postupu tak byly v databázi uloženy tři vrstvy s rozdílnými názvy, ale totožným obsahem. Důležité je zejména to, že jim zůstala zachována číselná označení uzlů (uvedena v atributové tabulce ve sloupcích source a target). V dalším postupu pak bylo možné pro každou vrstvu nastavit rozdílné hodnoty ceny za projetí linie, možnost použití vrstvy vertices_tmp pro nalezení identifikačního čísla uzlu podle jeho názvu však zůstala zachována u všech tří liniových vrstev.
4.3.4 Nastavení ceny Poslední překážkou v používání nahraných dat pro hledání tří různých typů tras zůstává skutečnost, že u všech tří vrstev je stále nastavená cena tak, že odpovídá délce linie.
31
V zadané aplikaci používám funkci knihovny pgRouting, jenž jako cenu za projetí linie považuje sloupeček s názvem length (viz dále), u každé ze tří vrstev je tedy třeba upravit hodnotu uloženou ve sloupci length. Zpočátku jsem uvažoval o hledání časově nejvýhodnější trasy, což by zahrnovalo určení průměrné rychlosti, pomocí níž by se vypočítal čas potřebný pro projetí dané linie. Tento postup však není vhodný, protože v této aplikaci nejde o nalezení trasy pouze nejrychlejší nebo pouze nejkratší, ale jde o nalezení nejkratší trasy při preferenci určitých typů cest, přičemž preference určitých typů cest je nejdůležitější. Při takovém požadavku by vznášet zároveň požadavek na nejrychlejší trasu bylo protichůdné, zejména v případě hledání trasy pro horské kolo. Trasa po asfaltové silnici bude totiž vždy rychlejší než stejně dlouhá trasa po kamenité cestě. Horské kolo si však málokdo kupuje pro ježdění po silnicích. Data OpenStreetMap obsahují celkem 15 typů cest, zdrojové kódy 5, 6 a 7 ukazují úpravy sloupce length (ceny za projetí linie) u jednotlivých typů v případě všech tří vrstev. U každé vrstvy jsem některým typům cest ponechal původní hodnotu sloupce length, čímž byly vymezeny preferované typy cest pro danou vrstvu. V případě ostatních typů cest jsem pak sloupec length násobil takovým číslem, aby se vhodně snížila pravděpodobnost využití této cesty. Ve sloupci length byla původně uložena délka linií. Nyní například u silnic první třídy vynásobím sloupec length dvěma a u silnic druhé třídy ponechám původní hodnotu. Předpokládejme trasu mezi dvěma body, kterou lze celou absolvovat po silnicích první třídy, ale také je možné zvolit variantu, která povede pouze po silnicích druhé třídy. Jiné cesty v tomto příkladu neuvažujme. Po vynásobení sloupce length dvěma u všech silnic první třídy je každý úsek takové silnice při hledání trasy považován za dvakrát delší, než ve skutečnosti je. A pokud u silnic druhé třídy byla ponechána původní hodnota sloupce length, tedy skutečná délka linie, musela by být trasa po silnicích první třídy o polovinu kratší než varianta po silnicích druhé třídy, aby ji program zvolil jako nejvýhodnější.
32
UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE
road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike road_bike
SET SET SET SET SET SET SET SET SET SET SET SET SET SET SET
length length length length length length length length length length length length length length length
= = = = = = = = = = = = = = =
2*delka WHERE 1000000*delka 1000000*delka 1000000*delka 1000000*delka 1000000*delka 1000000*delka 1000000*delka 2*delka WHERE 1000000*delka 2*delka WHERE 1000000*delka 1000000*delka 1000000*delka 2*delka WHERE
type='primary'; WHERE type='construction'; WHERE type='cycleway'; WHERE type='footway'; WHERE type='ford'; WHERE type='path'; WHERE type='pedestrian'; WHERE type='platform'; type='primary_link'; WHERE type='proposed'; type='residential'; WHERE type='service'; WHERE type='steps'; WHERE type='track'; type='unclassified';
Zdrojový kód 5: Nastavení sloupce length tabulky road_bike.
Před spuštěním SQL dotazů ve zdrojovém kódu 5 jsem do každé ze tří tabulek vložil sloupec s názvem délka a zkopíroval do něj hodnoty ze sloupce length. Použil jsem postup vycházející ze zdrojového kódu 4. Důvodem po provedení tohoto kroku bylo, že po změnění sloupce length bych musel případné informace o délce linie získávat opět pomocí funkce ST_Length(), tak jsem je raději uložil do nového sloupce. Tento nový sloupec je pak využit i v SQL dotazech, které upravují hodnoty sloupce length v jednotlivých tabulkách. Nyní se budu věnovat úpravám provedeným v tabulce road_bike, tedy tabulce pro hledání trasy pro silniční cyklistiku. Pro myšlenku, na základě níž jsem dospěl k použitému způsobu úprav sloupce length, je předpoklad, že trasu pro silniční kolo využije především aktivní cyklista, který jezdí na časté vyjížďky a nevyžaduje nic kromě kvalitního asfaltu a možnosti jet tak rychle jak zrovna chce, aniž by musel svému okolí věnovat více pozornosti než je nezbytně nutné. Pokud má řetězec uložený ve sloupci type (tedy typ cesty) hodnotu primary, primary_link, residential, nebo unclassified, je hodnota uložená ve sloupci length nastavena na dvojnásobek hodnoty uložené ve sloupci delka (protože sloupce length a delka měly před prováděním SQL dotazů uvedených ve zdrojovém kódu 5 stejnou hodnotu, je tak vlastně hodnota sloupce length nastavena na dvojnásobek původně uložené hodnoty). Uvedené typy cest reprezentují silnice první třídy (primary), nájezdy na silnice první třídy (primary_link), ulice v rezidenčních oblastech sídel (residential) a nezařazené typy (unclassified). Použití silnic první třídy jsem omezil kvůli nižšímu požitku z jízdy na silnici s hustým provozem, kdy si cyklista musí neustále hlídat stopu co nejblíže k okraji silnice. Silnice první třídy jsem však z použití pro silniční kolo nevyloučil zcela, protože při kondiční vyjížďce hustý provoz tolik nevadí a zkušený cyklista nemá s držením stopy problémy. Na 33
ulicích v rezidenčních oblastech sídel často pobíhají děti a psi, navíc přes auta zaparkovaná u silnice jsou špatně vidět. Cyklista tak musí stále bedlivě sledovat své okolí a nejet příliš rychle, což opět snižuje komfort jízdy. Nezařazené typy cest měly často nekvalitní povrch, proto také nejsou příliš vhodné pro jízdu na silničním kole. Po všech zde uvedených typech cest však na silničním kole je možné relativně bezpečně jet, proto jsem jen zvýšil jejich cenu, ale ne natolik aby byly zcela vyřazeny z používání. Typy cest s názvy construction, cycleway, footway, ford, path, pedestrian, platform, proposed, service, steps a track byly vynásobením sloupce length číslem 1000000 zcela vyřazeny z používání. Knihovna pgRouting by měla ignorovat linie, u nichž je cena nastavena na zápornou hodnotu. Tuto vlastnost však nebylo možné využít, protože při nastavení ceny u některé linie na zápornou hodnotu a následném spuštění dotazu na nejvýhodnější trasu server PostgreSQL spadne. Na tuto skutečnost jsem narazil při testování a později jsem si o ní přečetl i v Techer (2008), nejedná se tedy o problém, který by existoval pouze u mě. Bylo tedy nutné sloupec length u nežádoucích typů cest vynásobit číslem dost vysokým na to, aby byla vždy výhodnější trasa vedoucí po jiných typech cest. U typu constuction (ve výstavbě) je omezení použití logické – nelze jezdit po silnicích, které ještě nebyly dostavěny. Typ cycleway, označující cyklostezku, byl vyřazen proto, že některé úseky cyklostezek vedou i mimo asfaltovou cestu, což je povrch nevhodný pro jízdu na silničním kole. Footway označuje pěšinku určenou pro pěší chůzi (například cestu okolo kostela na Zelené hoře ve Žďáru nad Sázavou. Ford znamená anglicky brod, více snad není třeba dodávat. Cesty označená jako path už značí úzké pěšinky, které už vyžadují použití spíše horského kola a také určitou zkušenost jezdce s jízdou v náročném terénu. Pedestrian značí pěší zóny, zde je tedy provozování jakékoliv cyklistiky nežádoucí. Jako platform jsou označena nástupiště na autobusovém nádraží. Proposed označuje silnice, jejichž výstavba je teprve v plánu a ještě nezačala. Jako service jsou označeny různé „servisní“ silnice, například silnice vedoucí k nástupištím na autobusových nádražích. Slovem steps jsou označeny schody. A nakonec track je lesní nebo polní cesta dost široká na pro jetí traktoru nebo jiné zemědělské či lesnické techniky. Ostatním typům cest, jmenovitě road, secondary, secondary_link a tertiary byla ponechána původní hodnota sloupce length a jsou tedy preferovanými typy cesty pro silniční cyklistiku. Road značí silnici neupřesněného typu, secondary silnici druhé třídy a secondary_link nájezd na silnici druhé třídy (v tomto případě se jedná 34
spíše o složitější řešení křižovatky než nájezd v pravém slova smyslu, jako například nájezd na dálnici) a tertiary označuje silnice třetí třídy. Tyto typy cest jsou díky asfaltovému povrchu a nižšímu provozu nejvhodnější pro silniční cyklistiku, proto jsou nejvíce preferovány. UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE
trek_cross trek_cross trek_cross trek_cross trek_cross trek_cross trek_cross trek_cross trek_cross trek_cross trek_cross trek_cross
SET SET SET SET SET SET SET SET SET SET SET SET
length length length length length length length length length length length length
= = = = = = = = = = = =
5*delka WHERE type='primary'; 5*delka WHERE type='primary_link'; 10000000*delka WHERE type='construction'; 10*delka WHERE type='footway'; 10*delka WHERE type='ford'; 10000000*delka WHERE type='path'; 10*delka WHERE type='pedestrian'; 10000000*delka WHERE type='platform'; 10000000*delka WHERE type='proposed'; 10000000*delka WHERE type='service'; 20*delka WHERE type='steps'; 1.5*delka WHERE type='track';
Zdrojový kód 6: Nastavení sloupce length tabulky trek_cross.
Pro hledání trasy pro trekové a crossové kolo jsem sloupec length upravil SQL dotazy uvedenými ve zdrojovém kódu 6. Zcela vyloučeny z použití jsou zde jen silnice ve výstavbě (construction), cesty s označením path (jízda po nichž vyžaduje nejen horské kolo, ale také klade zvýšené nároky na techniku jízdy a zkušenosti jezdce), linie označující nástupiště (platform), plánované silnice (proposed) a servisní cesty (service). Provedené úpravy vycházejí z předpokladu, že typ trasy pro trekové a crossové kolo bude využíván především pro rekreační cyklistiku a cyklosturistiku. Jízda po silnicích první třídy byla omezena výrazněji než v případě silničních kol, ale v případě, že objížďka by byla velmi dlouhá, bude silnice první třídy použita. Omezeno je i použití pěšin s označením footway, nejsou však zcela vyloučeny, protože treková a crossová kola jsou často používána pro cykloturistiku, v případě takové aktivity by cyklista neměl mít problém přizpůsobit rychlost jízdy provozu chodců a případně i z kola sesednout. Vedení trasy přes brody je sice také omezeno, ale stále je reálně možné – v rámci cyklistického výletu není občasné sesednutí z kola na škodu a brod podle mého názoru není taková překážka, aby bylo kvůli ní nutné volit mnohonásobně delší trasu. Možnost plánování trasy přes pěší zónu (pedestrian) jsem zachoval ze stejného důvodu jako u pěšiny s označením footway. V případě použití tabulky trek_cross pro hledání nejvýhodnější trasy může trasa vést i přes schody. Vzhledem k tomu, že byla vynásobena už relativně vysokým číslem, by k tomu však došlo jen v extrémním případě. Zcela vyloučena tato možnost nebyla proto, že kolo lze přes 35
schody relativně snadno přenést, jak však již bylo zmíněno, trasa by přes schody vedla jen v extrémním případě. Posledním typem cesty, u nějž jsem upravoval sloupec length v tabulce trek_cross, je track. Sloupec je zde násoben jen číslem 1,5, což umožňuje relativně časté využívání těchto širokých lesních cest. Typům cycleway, residential, road, secondary, secondary_link, tertiary a unclassified byla zachována původní hodnota sloupce length, protože nic nebrání jejich využití pro rekreační cyklistiku, respektive z typů cest nacházejících se na testovacím území jsou pro rekreační cyklistiku a cykloturistiku nejvhodnější. Nakonec jsem provedl úpravy v tabulce mtb. Pro rozhodování o nastavení ceny u jednotlivých typů cest byla klíčová myšlenka, že cyklista jezdící na horském kole nebude chtít jet po asfaltu dál, než je nezbytně nutné. Zároveň se bude chtít vyhnout úsekům, kde by musel přizpůsobovat rychlost ostatním účastníkům provozu, protože, podobně jako v případě cyklisty jedoucího na silničním kole, jezdí zejména kvůli zlepšení kondice. Dotazy SQL použité pro úpravy sloupce length v tabulce mtb jsou na zdrojovém kódu 7. UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE UPDATE
mtb mtb mtb mtb mtb mtb mtb mtb mtb mtb mtb mtb mtb mtb mtb
SET SET SET SET SET SET SET SET SET SET SET SET SET SET SET
length length length length length length length length length length length length length length length
= = = = = = = = = = = = = = =
10000000*delka WHERE type='construction'; 2*delka WHERE type='cycleway'; 100*delka WHERE type='pedestrian'; 10000000*delka WHERE type='platform'; 10000000*delka WHERE type='primary'; 10000000*delka WHERE type='primary_link'; 10000000*delka WHERE type='proposed'; 2*delka WHERE type='residential'; 10*delka WHERE type='road'; 10*delka WHERE type='secondary'; 10*delka WHERE type='secondary_link'; 10000000*delka WHERE type='service'; 1.5*delka WHERE type='steps'; 2*delka WHERE type='tertiary'; 2*delka WHERE type='unclassified';
Zdrojový kód 7: Nastavení sloupce length tabulky mtb.
Z hledání trasy pro horské kolo byly vyloučeny typy cest s označením construction, platform, primary, primary_link, proposed a service. V případě typů construction, platform, proposed a service jsou důvody stejné jako u předchozích dvou vrstev. Silnice první třídy byly vyloučeny kvůli tomu, že jízda na horském kole po asfaltu už tak není nic příjemného a na silnici první třídy je navíc hustý provoz, je proto lepší se jí úplně vyhnout. Mírné omezení pro používání cyklostezek bylo zavedeno z důvodu častého hustého provozu cyklistů a nutnosti v takovém případě přizpůsobit rychlost jízdy situaci. Použití pěších zón je velmi výrazné, nejsou však vyloučeny zcela, protože 36
kvůli výraznému zkrácení vzdálenosti by i jezdci na horském kole mohlo stát za to na chvíli sesednout. Omezení u typu residential má v tomto případě stejný důvod jako v případě trasy pro silniční kolo, tedy nutnosti často na takových silnicích přizpůsobovat rychlost jízdy situaci. Omezení však není výrazné, protože cesty typu residential často navazují na různé lesní cesty, ke kterým by byl jinak cyklista veden jinudy, nebo by se mohlo stát, že by trasa vedla po větších cestách jen kvůli tomu, aby se vyhnula cestám typu residential. U typu road má omezení důvod v tom, že není uvedeno, o jaký typ silnice se jedná, lze tedy hůře odhadnout, jaký na ní bude provoz. Proto má typ road stejné omezení jako silnice druhé třídy, na nichž je provoz přeci jen trochu výraznější než na silnicích třetí třídy. Jízda po schodech je omezená minimálně, protože schody jsou mezi jezdci na horských kolech oblíbenou překážkou a jejich sjíždění nebo vybíhání s kolem na rameni může být vhodným způsobem přípravy pro jízdu v terénu. Ale vzhledem k tomu, že schody často navazují na pěší zónu nebo silnici typu residential, bude jejich použití omezeno i touto skutečností. Hodnota 1,5, kterou byl sloupec length vynásoben, je tak podle mého názoru adekvátní. Silnice třetí třídy a typ silnic unclassified byly vynásobeny číslem 2, protože u nich neočekávám vysoký provoz a protože by často nebylo možné najít trasu vedoucí jen po cestách bez asfaltu. Jsou tedy zvoleny jako jakési menší zlo při přesunu do těžšího terénu.
4.4 Tvorba uživatelského prostředí aplikace V kapitole 4.1 zmiňuji, že uživatelské prostředí zpracovávané aplikace je tvořeno jednou webovou stránkou. Zdrojový kód této stránky je možné rozdělit na několik částí. První částí je skript, psaný v jazyce Javascript, který definuje funkci init(), při jejímž spuštění je vytvořena a následně na webové stránce zobrazena mapa. Tato mapa obsahuje jako podkladovou vrstvu mapu OpenStreetMap a (v případě, že aplikace byla použita pro hledání trasy) vrstvu s nalezenou trasou. Tato část obsahuje také části psané v jazyce PHP. Za tímto skriptem následuje tělo webové stránky, v němž je spuštěna funkce vytvářející mapu a vytvořená mapa je pak zobrazena na 100 % výšky a 80 % šířky stránky. Dále se v těle stránky nachází formulář, do kterého uživatel zadá, odkud a kam chce vyhledat trasu a zvolí typ trasy, kterou si přeje vyhledat. Další formulář pak po odeslání zobrazí podkladovou mapu bez vyhledané trasy.
37
Poslední částí zdrojového kódu je pasáž napsaná v jazyce PHP. Tato pasáž po odeslání formuláře, který slouží pro zadání údajů o požadované trase, provede vyhledání nejvýhodnější trasy a uloží ji jako samostatnou vrstvu. V následujících podkapitolách se budu podrobně věnovat každé ze zmiňovaných částí zdrojového kódu. Pořadí, ve kterém jednotlivé části zdrojového kódu následují za sebou, neodpovídá pořadí, v jakém jsou uváděny v této práci. Jejich pořadí bylo změněno pro větší přehlednost při jejich popisování. Kompletní zdrojový kód je dostupný v příloze. Nedílnou součástí tvorby uživatelského prostředí je také vytvoření mapfilu, který umožní zobrazení vrstvy s nalezenou trasou pomocí Mapserveru.
4.4.1 Skript OpenLayers Skript uvedený ve zdrojovém kódu 8 vytváří funkci init(), po jejímž spuštění se vytvoří interaktivní mapa, kterou bude možné zobrazit na webové stránce. První řádek skriptu, vytvoří funkci init(). Tato funkce po spuštění inicializuje tvorbu mapy a umožní její zobrazení v prohlížeči (Hazzard, 2009). Na druhém řádku je definována proměnná bounds, do níž je uložena nová instance třídy OpenLayers.Bounds(), která reprezentuje hranice území. Uloženy v ní jsou souřadnice levého spodního rohu a pravého horního rohu v jednotkách použitého kartografického zobrazení. Instance třídy OpenLayers.Bounds() se využívá pro určení rozsahu území například u vlastnosti restrictedExtent nebo maxExtent, viz dále. Na dalších řádcích je definována proměnná options. Do této proměnné je uložen řetězec, obsahující větší množství vlastností, později přiřazených k mapě (k objektu mapy). V prvním řádku je do této proměnné uložena informace o kartografickém zobrazení, které má být při vykreslení mapy použito. Kartografické zobrazení je identifikováno kódem EPSG. Zobrazení EPSG: 900913 bylo použito proto, že je v něm standardně zobrazována použitá podkladová mapa OpenStreetMap (OpenLayers, [2010?]b). Na dalším řádku je pomocí vlastnosti restrictedExtent a proměnné bounds nastaven rozsah území, po němž bude možné mapu posouvat (OpenLayers, [2010?]a). Při použití vlastnosti restrictedExtent je posouvání mapy omezeno na zadaný rozsah území a při oddálení mapy nad rozsah zadaného území je zadané území vždy uprostřed. Následuje vlastnost controls, do které jsou předány ovládací prvky mapy. 38
V případě zpracovávané aplikace byl použit objekt OpenLayers.Control.Navigation(), který umožňuje ovládání mapy myší (OpenLayers, [2010?]a) a objekt OpenLayers.PanZoomBar(), jenž se skládá ze šipek umožňujících posunutí zobrazeného výřezu a z lišty, pomocí níž uživatel kontroluje úroveň přiblížení (OpenLayers, [2010?]a). Ovládací prvky lze do mapy přidat buď pomocí vlastnosti controls, nebo je lze přidat až po vytvoření mapy (Hazzard, 2010b). Následně je definována proměnná map. V této proměnné je vytvořena nová instance objektu OpenLayers.Map(). Jako parametry jsou předány název této instance (tedy v tomto případě „map“) a proměnná options, v níž jsou uloženy již popisované vlastnosti. Objekt OpenLayers.Map() obsahuje mapu. Samotný tento objekt však nelze prakticky využít. Lze do něj však přidat ovládací prvky a vrstvy, čímž vznikne interaktivní mapa. Přidávání ovládacích prvků bylo popsáno, přidávání vrstev bude naznačeno dále.
39
<script type="text/javascript"> function init(){ var bounds = new OpenLayers.Bounds(1759263, 6368375, 1810372, 6408685); var options = { projection: new OpenLayers.Projection("EPSG:900913"), restrictedExtent: bounds, controls: [new OpenLayers.Control.Navigation(), new OpenLayers.Control.PanZoomBar()], }; var map = new OpenLayers.Map('map', options); var route = new OpenLayers.Layer.WMS( 'route', "http://localhost/cgi-bin/mapserv.exe?", { map: 'C:/ms4w/Apache/htdocs/!testy/Hlinsko/routing_osm.map', layers: 'shortest', format: 'png', transparent: 'true' }, { maxExtent: bounds, projection: "EPSG:900913", units: 'm', isBaseLayer: false, opacity: 0.4 } ); var osm = new OpenLayers.Layer.OSM(); var hranice = new OpenLayers.Layer.Boxes("hranice"); var box = new OpenLayers.Marker.Box(bounds); hranice.addMarker(box); map.addLayer(hranice); map.addLayer(osm); map.zoomToMaxExtent();
}
Zdrojový kód 8: Skript OpenLayers.
Po proměnné map jsem v kódu definoval proměnnou route. V ní je vytvořena nová instance objektu OpenLayers.Layer.WMS. Tento objekt se používá pro uložení vrstvy WMS. Vrstvu vykreslenou Mapserverem tak lze snadno zobrazit v interaktivní mapě. První parametr určuje název vrstvy. Dále je uvedena cesta Mapserveru a za ní jsou definovány parametry, které jsou ve WMS žádosti předané Mapserveru. Mapserver tak zná cestu k mapfilu, jenž má použít, název vrstvy, kterou má vykreslit (název je definovaný v mapfilu), formát, v jakém by měla být výsledná mapa vykreslená a jestli bude rastrový obrázek s mapou průhledný. Průhlednost znamená, že v případě použití Mapserverem vykreslené mapy pro 40
překrytí podkladové mapy budou viditelné jen prvky jednotlivých vrstev a zbytek rastrového obrázku bude průhledný – tedy bude přes něj vidět podkladová mapa. Nakonec jsou definovány vlastnosti, které knihovně OpenLayers říkají, jak má mapu vykreslenou Mapserverem zobrazit. Je zde zadán rozsah území pomocí proměnné bounds (definované na začátku skriptu). Dále je zde určeno kartografické zobrazení pomocí kódu EPSG, mapové jednotky, je určeno, že tato instance objektu OpenLayers.Layer není podkladovou vrstvou a je definována průsvitnost vrstvy. Průsvitnost jsem nastavil, aby bylo možné přes vrstvu s nalezenou trasou vidět, po jakých cestách trasa vede. Průhlednost by se dala přirovnat ke kreslení na průsvitku. Pokud je zapnutá, Mapserver vykreslí zadané vrstvy „na průsvitku“ a po přiložení této „průsvitky“ na podkladovou mapu překrývají vykreslené vrstvy podkladovou mapu, ale přes zbytek průsvitky je podkladová mapa vidět. Kdyby zapnutá nebyla, Mapserver by kreslil zadané vrstvy „na papír“. Po přiložení mapy nakreslené na papíru přes jinou papírovou mapu však podkladovou mapu nevidíme. Proto je při použití mapy vykreslené Mapserverem pro překrytí podkladové vhodnější průhlednost zapnout. Vlastnost opacity, tedy průsvitnost, naproti tomu knihovně OpenLayers říká, že podkladová mapa musí být vidět i přes prvky nacházející se v mapě vykreslené Mapserverem. Průsvitnost je možné přirovnat k položení celé mapy na sklo, zpod nějž svítí na mapu světlo. Pokud je pak přes papírovou mapu položená průsvitka se zakreslenou trasou, tak i když by přes zakreslenou trasu nebylo normálně vidět, co se pod ní nachází v podkladové mapě, vhodně zvolené světlo umožní vidět podkladovou mapu i přes vrstvy zakreslené na průsvitce. Při nastavení hodnoty průsvitnosti na 0 není přes vrstvu podkladová mapa vůbec vidět a při použití hodnoty 1 naopak není vůbec vidět tato vrstva, jíž je podkladová mapa překryta, a která je nyní zcela průhledná. Průsvitnost vrstvy lze nastavit i v mapfilu, průsvitnost definovaná v mapfilu se však vztahuje jen na další vrstvy definované v tomtéž mapfilu. Tedy přes vrstvu s nastavenou průsvitností by byly vidět jen další vrstvy, které jsou v mapfilu definovány a jsou zobrazeny pod vrstvou s nastavenou průsvitností. Při položení této mapy pomocí knihovny OpenLayers přes podkladovou mapu by se však vrstva s průsvitností nastavenou v mapfilu jevila jen jako zobrazená méně sytou barvou a podkladová mapa by přes ni nebyla vidět.
41
Po definování proměnné route jsem vytvořil proměnnou osm a uložil do ní novou instanci objektu Openlayers.Layer.OSM. Pomocí tohoto objektu lze v mapové aplikaci zobrazit mapu OpenLayers (OpenLayers, [2010?]a). Na následujících třech řádcích je vytvořen polygon ohraničující testovací území. Velikost mapy na webové stránce jsem nastavil na 80% šířky a 100% výšky okna prohlížeče (viz dále). Což však v některých případech vytvoří mapu s poměrem stran neodpovídajícímu poměru stran testovacího území. Na konci skriptu je spuštěna funkce zoomToMaxExtent(), která nastaví přiblížení tak, aby bylo zobrazeno celé území vymezené ve vlastnosti maxExtent mapy nebo některé vrstvy. Pokud mapu nelze přiblížit tak, aby celou její plochu vyplňoval pouze zvolený výřez území, je zvoleno takové přiblížení, aby zvolený výřez území byl vidět celý. Což v případech, kdy je poměr stran mapy zobrazené v okně prohlížeče jiný než poměr stran zvoleného výřezu území, znamená, že je v mapě zobrazeno zvolené území, ale i jeho okolí. Znát rozsah testovacího území je pro uživatele důležité, protože jen v testovacím území je možné provádět vyhledávání trasy – pokud by jej tedy uživatel neznal, nemusel by si být jist, který bod ještě může použít pro vyhledávání a který už ne. Sice je pomocí vlastnosti restrictedExtent zamezeno posouvání mapy mimo povolené území, ale při použití nižší úrovně přiblížení (jako například při načtení mapy) nejsou hranice testovacího území nijak patrné. S tímto problémem se lze těžko vypořádat, protože každý uživatel má jiné rozlišení monitoru a tedy bude mít i jiný poměr stran okna prohlížeče. Bylo by nutné nastavit fixní velikost mapy v pixelech, ale to by mohlo dopadnout tak, že uživatelé s velkým rozlišením monitoru by viděli malinkou mapu uprostřed stránky a uživatelé s nízkým rozlišením monitoru by museli rolovat, aby viděli celou mapu. Proto jsem přistoupil k zobrazení obdélníku, jenž vymezí hranice testovacího území. Vykreslení hranic území by bylo možné vyřešit vytvořením vektorové vrstvy obdélníku s požadovaným rozsahem. Vrstvu by bylo možné uložit například ve formátu shapefile, vložit pomocí Mapserveru, nebylo by nutné ji ukládat do databáze PostGIS. V případě, že bych se rozhodl rozsah testovacího území měnit, bych však musel vytvořit novou vrstvu, takže takový postup by nebyl flexibilní. Z tohoto důvodu jsem použil postup uvedený na OpenLayers Examples ([2010?]) a vložil do mapy obdélník s rozsahem daným v proměnné bounds. Pokaždé, když bych chtěl měnit rozsah testovacího území, by tak stačilo přepsat pouze hodnoty
42
uložené v proměnné bounds a změnil by se jak rozsah obdélníku, tak rozsah uvedený u vlastností maxExtent a restrictedExtent. Nyní zpět ke kódu. V proměnné hranice je vytvořena nová instance objektu OpenLayers.Layer.Boxes, což je vrstva, v níž bude vykreslen obdélník určující hranice testovacího území. Následně je definována obdélníková značka (instance objektu OpenLayers.Marker.Box), které je pomocí proměnné bounds předán požadovaný rozsah. Poté je do instance hranice objektu OpenLayers.Layer.Boxes přidán obdélník vytvořený v proměnné box pomocí funkce addMarker. Objektu OpenLayers.Marker.Box je možné pomocí vlastnosti setBorder nastavit tloušťku a barvu obrysu, pro potřeby této práce jsem však ponechal přednastavené hodnoty – tedy šířku 2 pixely a červenou barvu (OpenLayers, [2010?]a). Nakonec jsou pomocí funkce addLayer přidány do instance objektu OpenLayers.Map s názvem map všechny požadované vrstvy. Vrstvy osm a hranice (tedy podkladová mapa OpenStreetMap a obdélník reprezentující hranice testovacího území) jsou do instance map přidány vždy. Vrstva route, obsahující vyhledanou trasu, je přidána jen pokud proměnné num_from a num_to nejsou rovny nule. Tyto proměnné obsahují údaje o počtu řádků tabulky nalezených podle názvů bodů, mezi nimiž chce uživatel hledat trasu a které zadal ve formuláři. Podmínka zajišťující přidání vrstvy route jen když bylo provedeno vyhledání trasy, je napsána v jazyce PHP a bude podrobněji popsána dále. Poslední řádek skriptu tvoří funkce zoomToMaxExtent(), která je zavolána pro instanci map. Tato funkce již byla zmíněna a nastaví přiblížení mapy tak, aby v ní bylo vidět celé území, jehož rozsah byl nastaven ve vlastnosti maxExtent vrstvy route a také ve vlastnosti restrictedExtent instance map. Ve zdrojovém kódu 9 je zobrazen řádek, který uvozuje tělo webové stránky a zároveň při načtení stránky provede spuštění funkce init(). Funkce init() vytvoří mapu, jejíž parametry byly zadány ve zdrojovém kódu 8 a připraví ji pro zobrazení v prohlížeči. Zdrojový kód 9: Spuštění funkce init().
Do webové stránky je mapa vložena zdrojovým kódem 10. Mapa se do webové stránky vkládá pomocí tagu div. Je požadováno zadání atributu id. Do atributu id se zadává název instance objektu OpenLayers.Map, která se má použít. Mapě je možné nastavit širokou škálu možností zobrazení pomocí stylů CSS. V tomto 43
případě jsem mapě nastavil šířku na 80% šířky okna prohlížeče (přednastaveno je 100%), aby se na pravou stranu vešel formulář pro zadání parametrů trasy. Dále je důležité nastavení umístění objektu doleva pomocí vlastnosti float: left, aby byl ostatním obsahem obtékán zprava. Následující tři vlastnosti nastaví použití plného rámečku o šířce 1 pixel a černé barvy okolo mapy.
Zdrojový kód 10: Vložení mapy s názvem map, vytvořené funkcí init() do webové stránky.
Po provedení úkonů popsaných v této kapitole jsem měl připravenou webovou stránku, na níž byla zobrazena mapa OpenStreetMap s obdélníkem vymezujícím hranice testovacího území. Této mapě bylo možné libovolně měnit měřítko a posouvat zobrazený výřez v rámci testovacího území.
4.4.2 Formulář pro uzpůsobení vyhledávané trasy Mimo samotné mapy je na webové stránce, představující uživatelské prostředí zpracovávané aplikace, ještě formulář. Do tohoto formuláře uživatel vloží názvy míst, mezi kterými chce hledat trasu a poté vybere typ trasy, jenž požaduje. Po odeslání formuláře je spuštěno vyhledání trasy a nalezená trasa následně zobrazena v mapě. Formulář byl vytvořen pomocí zdrojového kódu 11. První řádek kódu označuje blok, který obsahuje formulář. V atributu action je uveden odkaz na stránku, na níž bude prohlížeč přesměrován po odeslání formuláře. V tomto případě odkazuje formulář na tutéž stránku, z níž je odesílán. Je žádoucí, aby stránka s nalezenou trasou nebyla odlišná od stránky, z níž uživatel odesílá formulář, protože to by působilo zmatečně a nepřehledně. Místo dvou stránek, které vypadají téměř totožně, jsem tedy použil jednu s tím, že operace potřebné pro vyhledání trasy se provedou, jen když uživatel odešle formulář s platnými údaji. To bude popsáno v následující kapitole. V atributu method byla zvolena metoda GET.
44
Zdrojový kód 11: Formulář pro zadání údajů o požadované trase.
Po odeslání formuláře bude možné v jazyce PHP odkazovat na hodnoty vyplněné ve všech polích před odesláním pomocí proměnné. Protože formulář je odesílán pomocí metody GET, bude hodnota každého pole uložena v proměnné ve tvaru $_GET[název_pole]. Formulář je organizován pomocí tabulky, což pro tento účel byl nejjednodušší způsob jak jednotlivá pole formuláře zarovnat. První pole, do nějž uživatel vyplní název počátečního bodu trasy, má název from, po odeslání formuláře bude tedy hodnota v něm vyplněná dostupná v proměnné $_GET[from]. Následuje pole, do něhož by měl být vyplněn název cílového bodu trasy. Toto pole má název to. Poté je označen začátek výběrového pole, pojmenovaného route_type. Toto výběrové pole slouží pro výběr jednoho ze tří možných typů tras. Ve výběrovém poli jsou vloženy tři možnosti, z nichž každá reprezentuje jeden typ trasy. Po odeslání formuláře bude v proměnné $_GET[route_type] uloženo označení vybraného typu trasy a na základě hodnoty této proměnné bude vybrána tabulka, která bude použita pro hledání trasy. U každé z možností výběrového pole je vložena podmínka v jazyce PHP. Tato podmínka znamená, že po odeslání formuláře a opětovném načtení stránky bude ve výběrovém poli již označená možnost, kterou uživatel vybral při odesílání dotazu. Jedná se spíše o kosmetický prvek, jenž například umožní uživateli rychle vyhledat stejný typ trasy mezi jinými body aniž by musel znovu vybírat požadovaný typ trasy.
45
Po ukončení bloku výběrového pole a tabulky, v níž je formulář zarovnán, už následuje jen vložení tlačítka sloužícího pro odeslání formuláře. Na posledním řádku kódu je pak ukončen blok označující formulář. Za formulářem popsaným ve zdrojovém kódu 11 následuje ještě jeden, velmi krátký formulář, tvořený zdrojovým kódem 12. Slouží pro rychlé zobrazení aplikace tak, jak se zobrazí při prvním načtení prohlížeče, tedy bez nalezené trasy. Princip je velmi jednoduchý, formulář opět odkazuje na stejnou stránku, z níž je odesílán, nepředá však žádné proměnné. A protože veškeré operace související s hledáním trasy, ale také zobrazení vrstvy s nalezenou trasou, jsou prováděny, jen pokud byly odeslány platné názvy bodů, zobrazí se pouze mapa OpenStreetMap s obdélníkem vymezujícím hranice území.
Zdrojový kód 12: Formulář pro zrušení zobrazení nalezené trasy.
4.4.3 Hledání trasy Nyní zbývá jen popsat skript použitý pro zpracování dat z formuláře a následné vyhledání požadované trasy. Celý tento skript se nachází v hlavičce stránky. V jeho první části, uvedené ve zdrojovém kódu 13, jsou zpracována přijatá data z formuláře. Na začátku kódu jsem definoval proměnnou database, do níž jsem uložil všechny údaje potřebné pro připojení k databázi Hlinsko, jejíž tvorba byla popsána v kapitole 4.3. Následuje podmínka, která je splněna, pokud jsou stránce předány proměnné z polí formuláře from a to. Jinými slovy, tato podmínka je splněna, pokud uživatel vyplnil obě pole formuláře. Zde zmíním velmi důležitou informaci. V této podmínce je vložena ještě další část kódu, která je uvedená ve zdrojovém kódu 14. Tyto části kódu uvádím odděleně záměrně, protože každá má odlišný účel a při uvedení pohromadě by byl kód příliš dlouhý na to, aby se v něm šlo snadno zorientovat. Tečky na konci zdrojového kódu 13 značí, že před ukončením podmínky ještě kód pokračuje. Kompletní zdrojový kód je dostupný v příloze této práce.
46
$database = pg_Connect("host=localhost port=5432 dbname=Hlinsko user=postgres password=92670"); if($_GET[from] && $_GET[to]) { $encoding_query = "SET CLIENT_ENCODING TO 'WIN1250';"; $exec_encoding = pg_Exec($database, $encoding_query); $query_from = "SELECT id FROM vertices_tmp WHERE name = '$_GET[from]';"; $query_to = "SELECT id FROM vertices_tmp WHERE name = '$_GET[to]';"; $exec_from = pg_Exec($database, $query_from); $num_from = pg_NumRows($exec_from); $exec_to = pg_Exec($database, $query_to); $num_to = pg_NumRows($exec_to); $table = $_GET[route_type]; . . . } Zdrojový kód 13: Zpracování dat formuláře. Na místo teček na konci kódu patří kód uvedený ve zdrojovém kódu 14.
Provádění všech databázových dotazů PostgreSQL má v tomto skriptu dvě, resp. tři fáze. V první fázi je dotaz uložen do proměnné. V druhé fázi je uložením do proměnné spuštěna funkce pg_Exec(), jenž jako argumenty požaduje informace o připojení k databázi (uložené v proměnné database) a dotaz, který má provést (Momjian, 2003). Třetí fází je uložení výsledku dotazu do proměnné pomocí funkce pg_Result(). Tato funkce však nebyla ve zdrojovém kódu 13 použita, bude proto popsána později. Prvním spuštěným dotazem je dotaz uložený v proměnné encoding_query a spuštěný v proměnné exec_encoding. Tímto dotazem se změní používané kódování na WIN1250 (PostgreSQL: The world's most advanced open source diabase, c2005). Tento krok je nutný, protože názvy obcí ve vrstvě vertices_tmp jsou uloženy s diakritikou (tedy v kódování pro středoevropské jazyky), ale databáze byla vytvořena s kódováním UTF8, které českou diakritiku nepodporuje. Nastavením kódování na WIN1250 tak bude možno do formuláře zadávat i text s diakritikou (předtím databázový systém při odeslání formuláře, který obsahoval text s diakritikou, vrátil chybu). Dalším možným řešením by bylo vytvoření nové databáze s kódováním WIN1250. Tento krok by však byl časově náročný, upřednostnil jsem proto vložení těchto dvou řádků do zdrojového kódu aplikace.
47
Následující dva kroky ukládají do proměnné query_from, resp. query_to databázové dotazy. Tyto dva dotazy mají za úkol nalézt v tabulce vertices_tmp řádek, v němž hodnota uložená ve sloupci name odpovídá hodnotě nacházející se v proměnné $_GET[from], resp. $_GET[to] (tedy hodnotám odeslaným uživatelem z formuláře). Z vybraného řádku pak vybere hodnotu uloženou ve sloupci id. Jinými slovy, podle zadaného názvu bodu zjistí jeho id. Uložením do proměnné exec_from je pak spuštěna funkce pg_Exec(), která v tomto případě provede dotaz, uložený v proměnné query_from. Proměnná exec_from (tedy vlastně výsledek dotazu provedeného funkcí pg_Exec()) je pak použita jako argument pro funkci pg_NumRows(). Tato funkce vrátí číselnou hodnotu počtu výsledků dotazu, jenž má jako argument (PHP: PHP Manual, [2008?]). V proměnné num_from je pak uložena číselná hodnota, která udává počet výsledků dotazu uloženého v proměnné query_from a spuštěného v proměnné exec_from. Vzhledem k tomu, že názvy bodů v testovacím území jsou jedinečné, je při použitém SQL dotazu (text zadaný ve formuláři musí naprosto přesně odpovídat textu v atributové tabulce) hodnota vždy pouze 1 (v případě, že byl nalezen bod se shodným názvem) nebo 0 (v případě, že takový bod nalezen nebyl). Na následujících dvou řádcích je proveden shodný postup, jen pro proměnnou získanou z pole formuláře s názvem to. V posledním řádku kódu je hodnota, předaná z výběrového pole formuláře (tedy údaj o typu požadované trasy), uložena do proměnné table. Proměnná table je pak použita dále. V této části kódu byly tedy provedeny databázové dotazy na zjištění id bodu podle jeho názvu a byl získán počet výsledků těchto dotazů. Dále byla hodnota výběrového pole formuláře uložena do proměnné table. Další část kódu, zobrazená ve zdrojovém kódu 14, bezprostředně navazuje na kód ve zdrojovém kódu 13 – ten se nachází na místě, které je ve zdrojovém kódu 14 vyznačeno tečkami. Zdrojový kód 14 tedy stále patří do podmínky ověřující přijetí dat z textových polí formuláře.
48
if($_GET[from] && $_GET[to]) { . . . if($num_from != 0 && $num_to != 0) { $id_from = pg_Result($exec_from, 0, 0); $id_to = pg_Result($exec_to, 0, 0); $query_path = "BEGIN TRANSACTION;" . "DROP TABLE IF EXISTS shortest_path_table_1;" . "CREATE TABLE shortest_path_table_1(gid int4) with oids;" . "SELECT AddGeometryColumn( 'shortest_path_table_1', 'the_geom', -1, 'MULTILINESTRING', 2 );" . "INSERT INTO shortest_path_table_1(the_geom) " . "SELECT the_geom FROM dijkstra_sp_directed('$table','$id_from','$id_to','false','false');" . "END TRANSACTION;"; }
$result_path = pg_Exec($database, $query_path);
} Zdrojový kód 14: Vyhledání požadované trasy. Na místo teček na začátku kódu patří kód uvedený ve zdrojovém kódu 13.
Zdrojový kód 14 začíná ověřením, zda proměnné num_from a num_to obsahují nenulovou hodnotu – tedy jestli se každý z názvů zadaných v textových polích formuláře podařilo nalézt ve sloupci name tabulky vertices_tmp. Pokud je podmínka splněna, je kód v ní vložený spuštěn. Do proměnných id_from a id_to je pak uložena hodnota sloupce id z řádku obsahujícího název, zadaný v poli from, resp. to. To je provedeno pomocí funkce pg_Result(). Tato funkce má za argument proměnnou, v níž byl funkcí pg_Exec() spuštěn dotaz, jehož výsledek chceme znát. Další argumenty udávají, o kolik řádků níž a o kolik sloupců více vpravo sloupců od prvního řádku a sloupce výsledného výběru se nachází poptávané pole. Dotaz však vybírá pouze jeden řádek a jeden sloupec, na těchto pozicích jsou tedy vyplněny nuly. Následuje uložení série dotazů, které vytvoří novou tabulku (vrstvu) a vloží do ní geometrii nalezené trasy, do proměnné query_path. Celá série dotazů je prováděna v transakci, protože jejich provedení má smysl, jen pokud budou provedeny všechny. První dotaz v sérii zahajuje transakci. Druhý dotaz zkontroluje, zda v databázi existuje tabulka s názvem shortest_path_table_1 a pokud ano, tak ji vymaže. 49
Následně vytvoří tabulku s názvem shortest_path_table_1 a vloží do ní sloupeček s názvem gid, který bude obsahovat datový typ integer, tedy celá čísla. Tabulka shortest_path_1 bude také obsahovat identifikátory objektů (oids). Dále je do tabulky shortest_path_table_1 vložen sloupec s geometrií pomocí funkce AddGeometryColumn(). První parametr této funkce je název tabulky, v níž má být sloupec vytvořen. Ve druhém parametru je požadovaný název sloupce s geometrií. Dále by mělo být definováno kartografické zobrazení vrstvy, pomocí odkazu na jedno z kartografických zobrazení definovaných v tabulce spatial_ref_sys, kterou obsahuje každá databáze PostGIS. V tomto případě je použita hodnota -1, protože kartografické zobrazení není třeba definovat dopředu, do tabulky totiž budeme kopírovat data, jenž mají kartografické zobrazení už definováno. Předposlední parametr udává typ geometrie, který bude uložený ve vrstvě shortest_path_table_1, v tomto případě tedy liniové prvky. Nakonec je udáno, zda geometrie prvků bude dvourozměrná nebo trojrozměrná, tedy jestli body budou mít souřadnice x a y nebo x, y a z (kde z reprezentuje výšku, v níž se bod nachází). Do sloupce the_geom je pak vložen obsah sloupce the_geom z prvků vybraných při hledání trasy. Pro hledání trasy byla použita funkce dijkstra_sp_directed(), která trasu vyhledá pomocí Dijkstrova algoritmu. Jako parametr je jí pomocí proměnné table předán název vrstvy, v níž má trasu hledat a pomocí proměnných id_from a id_to identifikátory bodů mezi kterými trasu hledá. Nakonec je dvakrát uvedena hodnota false. První z nich znamená, že graf nemá sloupec reverse_cost a druhá že není orientovaný. V případě tohoto nastavení bude funkce dijkstra_sp_directed() hledat cenu ve sloupci s názvem length. Kdyby se oba parametry nastavily na true, používala by jako cenu hodnoty uvedené ve sloupcích cost a reverse_cost. Tabulky použité ve zpracovávané aplikaci sice sloupce cost a reverse_cost mají, ale vzhledem k tomu, že jsem nevytvářel orientovaný graf (tedy graf, kde by v každém směru byla nastavena jiná cena) je jednodušší modifikovat pouze sloupec length a pracovat pouze s ním, když to funkce dijkstra_sp_directed() umožňuje. Vložením obsahu sloupce the_geom prvků vybraných funkcí dijkstra_sp_directed() do tabulky shortest_path_table_1 vznikne liniová vrstva obsahující prvky vybrané při hledání trasy. Tedy vrstva, obsahující trasu. V proměnné query_path následuje už jen dotaz ukončující transakci.
50
V proměnné result_path je pak pomocí funkce pg_Exec spuštěn dotaz uvedený Připojení k databázi se nezdařilo"; Tento postup exit; uložení nalezené trasy byl převzat z Techer (2008). } else { if($_GET[from] && $_GET[to]) { if($num_from == 0) { echo "Zadaný počáteční bod nebyl nalezen
"; } if($num_to == 0) { echo "Zadaný cílový bod nebyl nalezen
"; } } } ?> Zdrojový kód 15: Ošetření některých možných chyb.
S hledáním trasy ještě nepřímo souvisí skript, který je uvedený hned na začátku těla stránky (viz zdrojový kód 15). Pokud se nepovede připojit k databázi, vypíše chybové hlášení a již se nebudou provádět žádné další akce. Jinak zkontroluje, jestli obdržel hodnoty obou proměnných z textových polí formulářů. Pokud ano, zkontroluje, jestli se mu podařilo v tabulce vertices_tmp nalézt řádek, v němž je ve sloupci name uložena hodnota odpovídající názvu bodu, zadaném ve formuláři (to je řešeno pomocí proměnných num_from a num_to definovaných ve zdrojovém kódu 13). Pokud jeden z bodů nenajde, vypíše odpovídající chybové hlášení.
4.4.4 Mapfile Poslední součástí uživatelského prostředí aplikace je mapfile, který umožňuje zobrazení vrstvy s nalezenou trasou Mapserverem. Zdrojový kód mapfilu je ve zdrojovém kódu 16. Ve zdrojovém kódu 8 je mapa, vykreslená Mapserverem podle tohoto mapfilu, vložena do mapové aplikace. Na začátku mapfilu je definován požadovaný formát výstupu a jeho rozsah. Rozsah je definován souřadnicemi levého spodního a pravého horního rohu. Dále je stanovena velikost rastrového obrázku a barva jeho pozadí. Barva pozadí se neprojeví, protože vykreslené mapa je v mapové aplikaci zobrazena jako průhledná. Poté je definováno kartografické zobrazení, ve kterém bude vykreslen mapový výstup. 51
MAP IMAGETYPE PNG EXTENT 1759263 6368375 1810372 6408685 SIZE 800 600 IMAGECOLOR 255 255 255 PROJECTION "init=EPSG:900913" END WEB
TEMPLATE 'OSM.php' IMAGEPATH '/ms4w/tmp/ms_tmp/' IMAGEURL '/ms_tmp/' METADATA "wms_abstract" "Trasa" "wms_title" "Trasa" "wms_srs" "EPSG:900913" END
END LAYER
CONNECTIONTYPE postgis CONNECTION "user=postgres password=92670 dbname=Hlinsko host=localhost port=5432" NAME "shortest" DATA "the_geom from shortest_path_table_1" STATUS ON TYPE LINE PROJECTION "init=EPSG:900913" END CLASS
END
NAME "shortest" STYLE COLOR 200 15 5 WIDTH 5 END
END END Zdrojový kód 16: Mapfile.
V objektu WEB je pak určen název šablony – tedy webové stránky, v níž bude výsledná mapa vykreslena. Šablona musí být v mapfilu určena pokaždé, když bude mapa zobrazována jako součást webové stránky a ne jen jako samotný rastrový obraz. V samotné šabloně – tedy webové stránce, na níž bude zobrazena vykreslená mapa – pak musí být uveden takzvaný „magický řetězec“. Tento řetězec je uveden ve zdrojovém kódu 17 a musí se nacházet na prvním řádku zdrojového kódu šablony (Mapserver [2010?]).
52
Zdrojový kód 17: „Magický řetězec“.
Vlastnosti IMAGEPATH a IMAGEURL říkají, kde jsou uloženy dočasné soubory obsahující rastrové obrázky s vykreslenou mapou. Vlastnost IMAGEPATH říká Mapserveru kam má obrázky uložit. Pomocí vlastnosti IMAGEURL pak Mapserver může předat prohlížeči informaci o tom, odkud může uložené obrázky načíst. Rozdíl v obou vlastnostech je v tom, že IMAGEPATH udává cestu v systému souborů serveru a IMAGEURL ukazuje sice na totožný adresář, ale pomocí adresy URL. V objektu METADATA jsou uloženy informace důležité pro použití mapy ve službě WMS (přes službu WMS je mapa vložená do mapové aplikace vytvořené knihovnou OpenLayers). Následují informace o jediné vrstvě, kterou tento mapfile zobrazuje. V prvních dvou řádcích jsou předány informace o typu vrstvy a přístupové údaje k databázi. Zapsání těchto údajů ve zdrojovém kódu mapfilu se na první pohled nejeví jako příliš bezpečné. Vždyť stačí, aby kdokoliv, kdo se v problematice mapserveru jen trochu orientuje, zobrazil zdrojový kód webové stránky s aplikací. Ve zdrojovém kódu pak snadno najde cestu k mapfilu a ten si poté může zobrazit. Snadno zjistit přístupové údaje k databázovému systému by tak mohl téměř kdokoliv. Řešením je uložit mapfile do adresáře, do něhož má přístup Apache (nebo jiný použitý webový server) i Mapserver, ale který není přístupný zvenčí. Vzhledem k tomu, že zpracovávaná aplikace nakonec nebude nikde zveřejňována a poběží jen na mém počítači, jsem však tento problém neřešil a mapfile je uložený ve stejném adresáři jako ostatní soubory s touto aplikací související. V objektu LAYER je dále zadána vlastnost NAME, která určuje název vrstvy (tento název vrstvy je použit ve skriptu OpenLayers). Názvy vrstev v mapfilu musí být jedinečné, protože slouží pro jednoznačnou identifikaci vrstvy. Ve vlastnosti DATA je uložena informace o tom, kde se nacházejí geometrická data vrstvy. V tomto případě je uveden sloupec the_geom tabulky shortest_path_table_1. Následně je aktivováno zobrazení vrstvy a určen typ její geometrie. V objektu PROJECTION jsem zadal zobrazení, v němž má být vrstva vykreslena. Uvnitř objektu CLASS je zadáno jméno třídy, které pro tuto aplikaci není důležité a je definován objekt STYLE. Objekt STYLE udává informace o stylu vykreslení vrstvy, v tomto případě je uvedena barva a tloušťka. Červenou barvu 53
vrstvy jsem zvolil proto, že je dobře vidět na podkladové mapě a vyšší tloušťku pak ze stejného důvodu. Tlustší čára by nevypadala dobře při zobrazení v malém měřítku a tenčí by se zase při použití většího měřítka ztrácela mezi ostatními mapovými prvky. Kompletní zdrojové kódy i použitá data jsou k dispozici na přiloženém CD, stejně jako instalační soubory použitých aplikací.
54
KAPITOLA 5 Výsledky Postupem popsaným v kapitolách 3 a 4 jsem vytvořil zadanou aplikaci. Tato aplikace má podobu webové stránky, v níž 100% výšky a 80% šířky zabírá interaktivní mapa. Zbytek šířky této stránky je zabrán formuláře, do nějž uživatel zadá údaje o požadované trase. Formulář má celkem jedno textové pole pro zadání počátku trasy a jedno textové pole pro zadání cíle trasy. Dále si může uživatel ve výběrovém poli zvolit ze tří možných typů tras: pro silniční, trekové nebo crossové a nebo horské kolo. Každá volba vyhledá jinou trasu po jiných typech cest. Nakonec je v aplikaci obsaženo i tlačítko, které zruší zobrazení nalezené trasy v interaktivní mapě. Trasu je možné hledat mezi sídly, která jsou zakreslena v datech OpenStreetMap a zároveň se nacházejí v testovacím území. Hledání trasy probíhá nad liniovými prvky zakreslenými ve vrstvě roads, jenž se nachází v souboru vektorových geografických dat OpenStreetMap. Rozsah testovacího území je dán dvěma body, které v kartografickém zobrazení označovaném jako Web Mercator mají souřadnice 1759263, 6368375 (levý spodní roh) a 1810372, 6408685 (pravý horní roh). Slovně lze území popsat jako část Českomoravské vrchoviny. Na západním okraji území se nachází město Ždírec nad Doubravou a na východním okraji město Polička. Severní okraj se pak nachází u nejsevernějšího bodu úseku silnice číslo I/34 mezi městy Hlinsko a Polička a na jižním okraji území leží města Žďár nad Sázavou a Nové Město na Moravě. Hranice tohoto území jsou v mapě označeny červeným obdélníkem. Obrázek 2 ukazuje, jak vypadá stránka s aplikací v prohlížeči Google Chrome, v němž byla primárně testována.
55
Obrázek 2: Vytvořená aplikace v prohlížeči Google Chrome
V případě vyplnění a odeslání formuláře provede aplikace hledání trasy s pomocí knihovny pgRouting. Nalezená trasa se uloží do databáze a v mapě bude zobrazena červenou barvou. Linie trasy je mírně průsvitná, aby byl vidět i obsah podkladové mapy nacházejícím pod ní.
Obrázek 3: Trasa pro silniční kolo mezi obcemi Herálec a Křižánky
Obrázek 3 ukazuje nalezenou trasu pro silniční kolo mezi obcemi Herálec a Křižánky. Nalezená trasa vede po silnicích druhé třídy číslo 350, 343 a 354. 56
Na obrázku 4 je znovu zobrazena nalezená trasa mezi oběma obcemi, tentokrát však pro trekové kolo. Trasa je vedena mezi stejnými body, tentokrát však po lesních cestách, které jsou v datech OpenStreetMap označeny jako track.
Obrázek 4: Trasa pro trekové kolo mezi obcemi Herálec a Křižánky
Na obrázku 5 je pak trasa pro horské kolo mezi týmiž body. Rozdíl oproti trase pro trekové kolo spočívá v tom, že trasa pro horské kolo je z části vedena po cestách typu path, které byly z hledání trasy pro trekové kolo vyloučeny.
Obrázek 5: Trasa pro horské kolo mezi obcemi Herálec a Křižánky
57
Jak je vidět na obrázcích 3, 4 a 5, aplikace splňuje svůj účel v preferenci určitých typů cest při hledání trasy pro různé druhy cyklistiky. Trasa pro silniční kolo je vedena výhradně po asfaltových silnicích. Trasa pro trekové kolo je vedena po lesních cestách, které však jsou snadno sjízdné. A trasa pro horské kolo vede i po cestách, jejichž projetí nebude mít cyklista úplně „zadarmo“.
58
KAPITOLA 6 Diskuze a závěr Hlavním cílem práce bylo vytvořit webovou mapovou aplikaci, jenž by uměla hledat nejvýhodnější trasu pro různé typy cyklistky, s využitím Open Source a volně dostupných nástrojů a aplikací. Tento cíl se podařilo splnit, byť ne úplně dokonale. Onou nedokonalostí je skutečnost, že jsem při přípravě dat použil komerční software ArcMap. Tento krok však byl pro vytvoření aplikace nutný kvůli problémům se stažením Open Source nástroje, jehož použití by zamezilo nutnosti použít aplikaci ArcMap. Dalším nedostatkem pak je skutečnost, že aplikace nemohla být zveřejněna na serveru geo.natur.cuni.cz, jak je uvedeno v zadání. Server geo.natur.cuni.cz měl totiž v létě problémy s hardwarem a v době dokončování této práce nebylo jisté, zda bude vůbec investováno do jeho opětovného zprovoznění. Tvorba aplikace nebyla bezproblémová, jak ostatně plyne i z předchozího odstavce. Nejvíce problémů bylo způsobeno zejména dostupností informací o práci s aplikacemi a nástroji, které jsem při tvorbě aplikace používal. Práce s některými aplikacemi, jako je PostgreSQL nebo Mapserver, je velmi dobře popsána v knihách (Momjian, 2003; Mitchell, 2005 a Kropla, 2005). U ostatních nástrojů a aplikací jsem však musel spoléhat na dokumentaci dostupnou na jejich webových stránkách. Ta je však často velmi stručná a pokrývá jen naprosté základy. Další informace bylo tedy nutné hledat jinde. Potřebné informace o použitých aplikacích jsem nakonec vždy našel, myslím si však, že dostupnost informací patří mezi jejich největší slabiny. Veškeré informace dostupné na webu jsou totiž nekonzistentní a pro vyřešení jednoho problému bylo často nutné pročíst velké množství zdrojů, z nichž většina byla naprosto neužitečná, nebo dlouho naslepo prohledávat dokumentaci a doufat, že narazím na to, co potřebuji vědět. Pokud pominu špatnou dostupnost informací, není práce s použitými nástroji nijak složitá a možnosti jejich využití jsou velmi široké. 59
Samotná vytvořená aplikace cíle práce splňuje. Postup, použitý při její tvorbě však má jisté nevýhody, které by mohly při jiném než testovacím způsobu využití způsobit komplikace, zejména co se týká časové náročnosti. Jako příklad zmíním pojmenovávání bodů ve vrstvě vertices_tmp. Při rozloze testovacího území to netrvalo příliš dlouho. Pokud bych však chtěl zpracovávat data například pro celou Českou Republiku, trvala by příprava dat o hodně déle. V České Republice bylo v roce 2010 evidováno 6250 obcí (ČSÚ, 2011), což by v případě tvorby aplikace pro celou ČR znamenalo pojmenovávat 6250 bodů. To však lze těžko obejít a body jinak než ručně pojmenovat nepůjde. Co však je větší problém, je případná aktualizace dat. Kdybych například zjistil, že v datech OpenStreetMap přibylo v testovacím území větší množství nových cest, určitě bych chtěl data v aplikaci aktualizovat. Stáhnul bych tedy podkladová data znovu, ale pro použití s knihovnou pgRouting bych musel znovu projít celý postup popsaný v kapitole 3. Vytvoření potřebných sloupců v tabulce není takový problém. Ale funkce assign_vertex_id() pokaždé přiřadí témuž bodu jiný číselný identifikátor. To znamená, že bych pro získání číselného identifikátoru bodu podle jeho názvu nemohl použít starou tabulku vertices_tmp, ale musel bych vytvořit novou a všechny body znovu pojmenovat. Což by znamenalo opakovat časově nejvíce náročnou část tvorby aplikace pokaždé, když bych aktualizoval data. Pro plnohodnotné dlouhodobé používání aplikace, které by nutně znamenalo pravidelnou aktualizaci podkladových dat, by tedy bylo nutné nalézt jiný způsob jak funkci pro vyhledání trasy předat číselné identifikátory bodů, zatímco uživatel by zadával jejich název. I přes uvedené nevýhody lze popsaný postup použít, při vytváření podobné aplikace nad menším územím není časová náročnost přípravy dat nijak drastická. Pro zpracování většího území lze použít nástroje a aplikace, které byly použity v této práci, pokud však bude nutné data pravidelně aktualizovat, bylo by vhodné najít postup, který bude vyžadovat méně času. Použití Open Source nástrojů přináší výhody hlavně autorovi aplikace, kterému šetří finanční náklady. Přínos pro uživatele tkví ve způsobu, jakým aplikace hledá trasu. Použitý algoritmus je sice běžně známý a používaný, způsob nastavení ceny jednotlivým typům cest však umožňuje nalezení trasy s preferencí určitého typu cesty, což je možnost, jenž u ostatních vyhledávačů trasy chybí nebo se vyskytuje jen velmi omezeně.
60
Seznam zdrojů informací ČESKÝ STATISTICKÝ ÚŘAD. 2011. Územně analytické podklady ČSÚ [online]. 2011, poslední aktualizace: 30, .6. 2011 [cit. 2011-08-05] . Dostupné z WWW:
. GILMORE, James W. 2005. Velká kniha PHP 5 a MySQL : kompendium znalostí pro začátečníky i profesionály. 1. vyd. Brno : Zoner Press, 2005. 711 s.ISBN 8086815-20-X. HAZZARD, Erik. 2009. OpenLayers Tutorial – Part 1 – Introduction [online]. 2009, poslední revize 3. 10. 2009 [cit. 2011-07-30]. Dostupné z WWW: . HAZZARD, Erik. 2010a. Dijkstra’s Algorithm – Shortest Path [online]. 2010a, poslední revize 17. 1. 2010 [cit. 2011-07-31]. Dostupné z: . HAZZARD, Erik. 2010b. OpenLayers Tutorial – Part 3 – Controls [online]. 2010b, poslední revize 2. 5. 2010 [cit. 2011-08-03]. Dostupné z WWW: . JANOVSKÝ, Dušan. [2000?] Jak psát web [online]. [2000?], poslední aktualizace 07. června 2011 [cit. 2011-08-06]. Dostupné z WWW: . ISSN 1801-0458. KROPLA, Bill. 2005. Beginning MapServer: Open Source GIS Development. 1st ed. USA: Apress, 2005. 417 s. ISBN 1-59059-490-8. Maps Help [online]. c2011 [cit. 2011-06-25]. What is My Maps and how do I use it?. Dostupné z WWW: . Mapserver [online]. [2010?] [cit. 2011-08-05]. Mapserver 6.0.1 Documentation. Dostupné z WWW: .
61
MITCHELL, Tyler. 2005. Web Mapping Illustrated. 1st ed. USA: O’Reilly, 2005. 349 s. ISBN 978-0-596-00865-9. MOMJIAN, Bruce. 2003. PostgreSQL: Praktický průvodce. 1. vyd. Brno: Computer Press, 2003. 402 s. ISBN 80-7226-954-2 OPEN SOURCE INITIATIVE. [2010?] Open Source Inititative [online]. [2010?] [cit. 2011-06-25]. About the Open Source Inititative. Dostupné z WWW: . OpenLayers [online]. [2010?]a [cit. 2011-08-03]. OpenLayers JavaScript API Documentation. Dostupné z WWW: . OpenLayers [online]. [2010?]b [cit. 2011-08-05]. OpenLayers Library Documentation. Dostupné z WWW: . OpenLayers Examples [online]. [2010?] [cit. 2011-08-04]. Setting a visual Extent. Dostupné z WWW: . PATRUSHEV, Anton. 2007. Shortest path search in real road network with pgRouting [online]. 2007 [cit. 2011-06-25]. Dostupné z WWW: . PgAdmin: PostgreSQL administration and management tools [online]. [2010?] [cit. 2011-07-31]. Introduction. Dostupné z WWW: . pgRouting Project [online]. [2010?]a [cit. 2011-08-05]. Creating Data for Routing Applications. Dostupné z WWW: . pgRouting Project [online]. [2010?]b [cit. 2011-06-18]. PgRouting Project. Dostupné z WWW: . pgRouting Project [online]. [2010?]c [cit. 2011-08-02]. Get your own routing data. Dostupné z WWW: . pgRouting Project [online]. [2010?]d [cit. 2011-06-18]. PgRouting Project. Dostupné z WWW: . PHP: PHP Manual [online]. [2008?] [cit. 2011-08-04]. Pg_num_rows. Dostupné z WWW: .
62
PostgreSQL: The world's most advanced open source database [online]. c2005 [cit. 2011-08-02]. PostgreSQL: Documentation: Manuals: PostgreSQL 8.1: Character Set Support. Dostupné z WWW: . REFRACTIONS RESEARCH. [2010?]. PostGIS 1.5.2 Manual [online]. [2010?] [cit. 2011-06-25]. Dostupné z WWW: . TECHER, Jean David. 2008. pgRouting 1.02 on Win32 [online]. 2008, poslední revize 12. 7. 2008 [cit. 2011-07-31]. Dostupné z WWW: .
63
Seznam příloh Příloha 1
CD s elektronickou verzí této práce, zdrojovýmí kódy a použitými daty
64