§
Razvoj naprednih storitev za GIS
6 Podatkovni model

Zahvala Kazalo Seznam uporabljenih kratic in simbolov 1 Povzetek 2 Abstract 3 Uvod 4 Opis problema 5 Načrt sistema 6 Podatkovni model 7 Geokodiranje 8 Nasprotno geokodiranje 9 Usmerjanje 10 Izkušnje in nadaljnje delo 11 Sklep Dodatek A Navodila za uporabo sistema Literatura

+ Lastnosti dokumenta

Naslov
Razvoj naprednih storitev za GIS
Del
6 Podatkovni model
Datum vsebine
22. 11. 2009
Original
RazvojNaprednihStoritevZaGIS.pdf
Vrsta
diplomska naloga
Jezik
slovenščina
Različica
1.0
Ustanova
Fakulteta za računalništvo in informatiko, Univerza v Ljubljani
Študij
Univerzitetni, računalništvo in informatika, logika in sistemi
Predmet
-
Mentor
doc. dr. Mojca Ciglarič
Avtor
Tine Lesjak
Ocena
10 od 1-10

Gre za celotno diplomsko delo s predstavitvijo.

Dodatne objave dela:

+ Priloge

predstavitev.pdf
Predstavitev diplomskega dela na zagovoru (dne 22. 10. 2009).
projekti.zip
Vsa izvorna koda v obliki SpringSource Tool Suite projektov (združljivi z Eclipseom) pod licenco Creative Commons Attribution 2.5 Slovenia License
primeri.zip
Primeri, predstavljeni kot izvlečki kode v diplomskem delu.
tinel-gis-geocoding.war
Storitev geokodiranja.
tinel-gis-reversegeocoding.war
Storitev nasprotnega geokodiranja.
tinel-gis-routing.war
Storitev usmerjanja.

Glavni cilj podatkovnega modela je poenotenje strukture podatkov tako, da ga vse storitve lahko izkoriščajo in da se podatki nikoli ne podvajajo. S tem pridobimo na preglednosti (vse storitve uporabljajo isti model brez izjem) in enostavnem vzdrževanju.

Podatkovni model, ki je skupen več storitvam, zna biti zahtevna zadeva, sploh če želimo vanj vstaviti vrsto različnih geografskih podatkov iz različnih koncev sveta. Namreč, razlika v cestno naslovnem sistemu, npr., v ZDA in pri nas je kar precejšnja. Ravno zaradi tega ne obstaja idealen model, ki bi hkrati veljal za cel svet, temveč so GIS-i tipično lokalne narave.
Eden takšnih je opisan na spletnih straneh ESRI-ja [12], ki so ga izdelali za lokalne namene kanadskega mesta Calgary, a je bil v veliko pomoč pri snovanju tega.

Skušal sem narediti preprost podatkovni model, ki je primeren za večino dežel, njegova vrlina pa je predvsem enostavnost. Hrani podatke v različnih nivojih, relacijah. Model je karseda uniformen, načeloma lahko vanj vstavimo geografske podatke z vsega sveta. Je tudi zelo fleksibilen, tako da ga je možno razširiti z dodatnimi in zahtevnejšimi podatki.

6.1 Domena

Elementom, ki vsebujejo edinstvene (unikatne) podatke, v podatkovnem žargonu pravimo podatkovna domena (angl. data domain). V našem primeru so ti elementi na aplikacijski strani preprosto objekti, na strani podatkovne zbirke pa entitete. Med seboj se preslikujejo s pomočjo že opisane tehnologije objektno relacijskega preslikovanja ORM.

Domenski objekti za sistem so spisani v javinem paketu net.tinelstudio.gis.model.domain.

6.1.1 GeoName

Osrednji domenski objekt je GeoName. Predstavlja vsa takšna in drugačna geografska imena (geoimena) skupaj z vrsto podatka.

GeoName:

Vrsta podatka (type) je lahko naslov (ADDRESS), cesta/ulica (STREET), zgradba (BUILDING), mesto/vas (TOWN), regija/dežela/provinca/prefektura (REGION), država (COUNTRY) ali kontinent (CONTINENT). Podatki so čim bolj podobni Google Maps API-ju [18].
Novo vrsto podatka je možno enostavno dodati. Na primer, če imamo podatke o železnicah, bi dodali novo vrsto RAILWAY, ali če imamo podatke o vodnih linijah, kot so reke in potoki, bi dodali vrsto WATERLINE.

Vrsta STREET v grobem predstavlja imena cest oz. ulic, ki nimajo naslovov (avtoceste, magistralke) in imajo le oznake (npr. "A1", "E57"). Vrsta ADDRESS predstavlja imena cest oz. ulic, ki imajo ulične naslove (npr. "Tržaška cesta").
V imenu je za razliko od nekaterih drugih podatkovnih modelov zapisano kar celotno ime (skupaj s "cesta", "ulica" ipd.) in indeksirano po celem imenu. S tem se pri ugotavljanju polnih imen (npr. risanje cest na karto) izognimo težavnim delom, ko je lahko ulica zapisana na različne načine (npr. "Ulica 5. maja" in "Gubčeva ulica").

package net.tinelstudio.gis.model.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;

/**
 * The main entity providing only geographic names.
 *
 * @author TineL
 */
@Entity
public class GeoName extends BaseEntity {

  @Column(nullable=false)
  private String name;

  @Column(nullable=false)
  @Enumerated(EnumType.STRING)
  private Type type;

  /**
   * The GeoName types.
   *
   * @author TineL
   */
  public enum Type {

    /** The geographic name at the address level accuracy. */
    ADDRESS,

    /** The geographic name at the street level accuracy. */
    STREET,

    /** The geographic name at the building (premise, property) level
        accuracy. */
    BUILDING,

    /** The geographic name at the town (city, village) level accuracy. */
    TOWN,

    /**
     * The geographic name at the region (state, province, prefecture,
     * etc.) level accuracy.
     */
    REGION,

    /** The geographic name at the country level accuracy. */
    COUNTRY,

    /** The geographic name at the continent level accuracy. */
    CONTINENT
  }

  // ...
}

Koda 6.1: Domenski objekt GeoName. Getterji in setterji so izpuščeni.

6.1.2 Address

Naslov (Address) je eden najpomembnejših domenskih objektov. V prvi vrsti se uporablja tako rekoč povsod, pri vseh storitvah, saj vsebuje najbolj natančen in za uporabnike zanimiv podatek: lokacijo na hišno številko natančno.

Address:

Predstavlja en naslov s točno določeno geografsko lokacijo.

Zaradi fleksibilne zasnove (relacija več:več z objektom GeoName) lahko ima naslov več imen (ime ulice, ime mesta, ime države, ime kontinenta ...). Lahko ima tudi več imen iste vrste, npr., ulic. Prek geoimen s pravilno poizvedbo lahko hitro pridemo do, npr., dejanske ceste. To velja tudi za ceste in zgradbe.

package net.tinelstudio.gis.model.domain;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Type;

import com.vividsolutions.jts.geom.Point;

/**
 * The Address entity.
 *
 * @author TineL
 */
@Entity
public class Address extends NotedEntity {

  @Column(nullable=false)
  @Type(type="org.hibernatespatial.GeometryUserType")
  private Point point;

  @Column(nullable=false)
  @ManyToMany
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private Set<GeoName> geoNames;

  @Column(nullable=false)
  private String houseNumber;

  // ...
}

Koda 6.2: Domenski objekt Address. Getterji in setterji so izpuščeni.

6.1.3 Street

Domenski objekt Street (cesta) predstavlja vse ceste in ulice z naslovi ob levi in desni strani. En element je vedno odsek ceste od enega vozlišča (angl. node) do drugega. Odsek ceste je sestavljen iz ene ali več linij.
Križišče cest mora biti vozlišče, da bo storitev usmerjanja dajala pravilne rezultate.
Nekatere daljše ceste imajo veliko odsekov, ker imajo veliko križišč (npr. Tržaška, Celovška, Dunajska ipd.).
En odsek ceste lahko ima več imen (npr. več uličnih imen in dodatno še oznako (avto)ceste).

Street:

Atribut level pove velikost oz. pomembnost ceste, ki pride prav tako pri usmerjanju kot pri upodabljanju (angl. rendering) ceste na karto pri različnih povečavah (manjše ceste se ne rišejo pri majhnih povečavah). Za upodabljanje sta mišljena tudi atributa leftAddressRange in rightAddressRange.

package net.tinelstudio.gis.model.domain;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;

import net.tinelstudio.gis.common.dto.StreetDto.Level;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Type;

import com.vividsolutions.jts.geom.LineString;

/**
 * The Street entity.
 *
 * @author TineL
 */
@Entity
public class Street extends NotedEntity {

  @Column(nullable=false)
  @Type(type="org.hibernatespatial.GeometryUserType")
  private LineString lineString;

  @Column
  @ManyToMany
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private Set<GeoName> geoNames;

  @Column
  @ManyToMany
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private Set<AddressRange> leftAddressRanges;

  @Column
  @ManyToMany
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private Set<AddressRange> rightAddressRanges;

  @Column(nullable=false)
  private Integer lengthMeters;

  @Column(nullable=false)
  /*
   * Warning: Street levels are persisted as ordinal integers from
   * enumeration.
   * Add new enumeration literals only to the end, do not delete any, or
   * else levels will be confused in the DB.
   */
  @Enumerated(EnumType.ORDINAL)
  private Level level;

  @Column
  private Boolean oneWay;

  @ManyToOne(optional=false)
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private StreetNode startNode;

  @ManyToOne(optional=false)
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private StreetNode endNode;

  // ...
}

Koda 6.3: Domenski objekt Street. Getterji in setterji so izpuščeni.

AdressRange:

Predstavlja serijo naslovov, ki imajo nekaj skupnega: so na isti cesti (npr. od "Tržaška cesta 1" do "Tržaška cesta 125").

package net.tinelstudio.gis.model.domain;

import javax.persistence.Entity;
import javax.persistence.ManyToOne;

/**
 * The AddressRange entity.
 *
 * @author TineL
 */
@Entity
public class AddressRange extends BaseEntity {

  @ManyToOne(optional=false)
  private Address fromAddress;

  @ManyToOne(optional=false)
  private Address toAddress;

  // ...
}

Koda 6.4: Domenski objekt AddressRange. Getterji in setterji so izpuščeni.

StreetNode:

Predstavlja vse začetne in končne točke vseh odsekov cest. Uporablja se pri usmerjanju za iskanje povezav med odseki.

package net.tinelstudio.gis.model.domain;

import javax.persistence.Column;
import javax.persistence.Entity;

import org.hibernate.annotations.Type;

import com.vividsolutions.jts.geom.Point;

/**
 * The StreetNode entity.
 *
 * @author TineL
 */
@Entity
public class StreetNode extends BaseEntity {

  @Column(nullable=false)
  @Type(type="org.hibernatespatial.GeometryUserType")
  private Point point;

  // ...
}

Koda 6.5: Domenski objekt StreetNode. Getterji in setterji so izpuščeni.

Slika 6.1: Zgradba ceste. Slika prikazuje cesto, cestne odseke ali nize linij, vozlišča in serije naslovov.

6.1.4 Building

Zgradba (Building) je površinski geografski element, ki lahko predstavlja marsikaj: osebne hiše, poslovne objekte, javne centre in tovarne. Lahko ima več splošnih imen (npr. ime muzeja, bolnišnice, avtobusne postaje, gradu, zanimive zgradbe ...).

Building:

package net.tinelstudio.gis.model.domain;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Type;

import com.vividsolutions.jts.geom.Polygon;

/**
 * The Building entity.
 *
 * @author TineL
 */
@Entity
public class Building extends NotedEntity {

  @Column(nullable=false)
  @Type(type="org.hibernatespatial.GeometryUserType")
  private Polygon polygon;

  @Column
  @ManyToMany
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private Set<GeoName> geoNames;

  // ...
}

Koda 6.6: Domenski objekt Building. Getterji in setterji so izpuščeni.

Prav vsi domenski objekti imajo še atribut:

ki ga dedujejo od razreda BasicEntity ali NotedEntity. NotedEntity vsebuje poleg atributa id, še atribut note.

Identifikacija je entiteti primarni ključ.

Slika 6.2: Razredni diagram domenskih objektov.

Slika 6.3: Primer upodobitve geografskih krajev, ki jih predstavljajo domenski objekti. Rdeči kvadratki so naslovi, rumene črte so ceste, oranžni krogci so vozlišča in sivi liki so zgradbe.

6.2 Model entitetnih razmerij

Model entitetnih razmerij (angl. Entity-Relationship Model) je abstraktna predstavitev podatkov v konceptualni obliki. Uporablja se za načrtovanje in prikaz modela neposredno v podatkovni zbirki.
Ker je sistem zasnovan z druge strani, to je s strani aplikacije, se domenski objekti nezamenljivo preslikajo v entitete in razmerja med njimi (ORM). Model v sami podatkovni zbirki lahko ustvarimo ročno oz. prek skripte SQL. Lahko pa ustvarimo le prazno zbirko in vso ostalo delo prepustimo kar Hibernateu.

V skriptnem programskem jeziku Groovy, ki je soroden javi, sem napisal skripto, ki ustvari prazno podatkovno zbirko z vsemi entitetami, njihovimi razmerji in potrebnimi indeksi. Deli kode se lahko uporabijo za morebitno kasnejše spreminjanje modela, ko so podatki že vstavljeni.

Podatkovni model je v skladu s standardi OGC oz. v skladu s zahtevami PostGIS (ki upoštevajo standarde OGC). Ti med drugim definirajo dve obvezni dodatni tabeli [24]:

PostGIS obe tabeli aktivno interno uporablja, kadar kličemo njegove funkcije. Zato obstaja tudi posebna funkcija AddGeometryColumn, ki doda stolpec s prostorskimi podatki v tabelo. Slednje skripta, ki ustvari podatkovno zbirko z entitetami, upošteva.

Slika 6.4: Diagram entitetnih razmerij podatkovnega modela. Tabeli geometry_columns in spatial_ref_sys nista prikazani, ker sta predvsem administrativnega značaja.

6.3 Dostop do podatkov (DAO)

Sistem ima nekaj vnaprej pripravljenih vmesnikov, ki definirajo preizkušene dostope do podatkov v zbirki (DAO). Nahajajo se v javinem paketu net.tinelstudio.gis.model.dao.

Osnovni objekti DAO so za vsak domenski objekt posebej, definirajo pa tipične operacije za manipulacijo s podatki:

package net.tinelstudio.gis.model.dao;

import java.util.Collection;
import java.util.List;

/**
 * Defines a Domain Access Object (DAO) with typical load, save & delete
 * operations.
 *
 * @author TineL
 * @param <E> the domain object
 */
public interface LoadSaveDeleteDao<E> {

  E load(Long id);
  List<E> loadAll();

  void save(E element);
  void saveAll(Collection<E> elements);

  boolean delete(Long id);
  void deleteAll();
}

Koda 6.7: LoadSaveDeleteDao definira tipične operacije za manipulacijo objektov v podatkovni zbirki. Komentarji so izpuščeni.

Sistem ima v enem vmesniku, imenovanem FindingDao, definirane vse ključne, visoko učinkovite metode za iskanje podatkov, kar s pridom uporabljajo vse storitve. V enem vmesniku so zato, da ima razvijalec ob morebitni spremembe podatkovnega modela ali ob izboljšanju iskalnih poizvedb vse na enem mestu.

package net.tinelstudio.gis.model.dao;

import java.util.List;

import net.tinelstudio.gis.model.domain.Address;
import net.tinelstudio.gis.model.domain.Building;
import net.tinelstudio.gis.model.domain.Street;
import net.tinelstudio.gis.model.domain.StreetNode;
import net.tinelstudio.gis.model.domain.GeoName.Type;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;

/**
 * Defines a Domain Access Object with special finding operations.
 *
 * @author TineL
 */
public interface FindingDao {

  List<Address> findAddresses(List<String> names, List<String>
    houseNumbers, int maxResults);

  List<Address> findDistinctAddressesByType(List<String> names, Type type,
    int maxResults);

  List<Street> findStreets(List<String> names, int maxResults);

  List<Street> findDistinctStreetsByType(List<String> names, Type type,
    int maxResults);

  List<Building> findBuildings(List<String> names, int maxResults);

  List<Building> findDistinctBuildingsByType(List<String> names, Type type,
    int maxResults);

  List<Address> findNearestAddresses(Point point, int maxDistanceMeters,
    int maxResults);

  List<Street> findNearestStreets(Point point, int maxDistanceMeters,
    int maxResults);

  List<Building> findNearestBuildings(Point point, int maxDistanceMeters,
    int maxResults);

  StreetNode findNearestStreetNode(Point point, int maxDistanceMeters);

  List<Address> findNearestAddressesApprox(Geometry place,
    int maxDistanceMeters, int maxResults);

  List<Street> findNearestStreetsApprox(Geometry place, int
    maxDistanceMeters, int maxResults);

  List<Building> findNearestBuildingsApprox(Geometry place,
    int maxDistanceMeters, int maxResults);

  StreetNode findNearestStreetNodeApprox(Geometry place, int
    maxDistanceMeters);

  List<Street> findConnectedStreets(StreetNode streetNode);
}

Koda 6.8: FindingDao definira metode za iskanje ustreznih geografskih krajev po podatkovni zbirki. Komentarji so izpuščeni.

Vsi vmesniki so implementirani za uporabo s Hibernateom (nahajajo se v javinem paketu net.tinelstudio.gis.model.dao.hibernate). Na pomoč jim priskoči Springova predloga HibernateTemplate, s katero je možno napisati prav vse poizvedbe. HibernateTemplate hkrati še poenostavlja najbolj osnovno iskanje in skrbi za transakcije.

6.4 Morebitne razširitve

Primer bolj uporabne razširitve podatkovnega modela bi bila dodatna atributa v domeni zgradbe (Building):

@Column
@ManyToMany
@Cascade(value=CascadeType.SAVE_UPDATE)
private Set<Address> addresses;

@Column
@ManyToMany
@Cascade(value=CascadeType.SAVE_UPDATE)
private Set<Street> streets;

Koda 6.9: Dodatna atributa addresses in streets v domenskem objektu Building.

Zgradba v resničnem svetu načeloma lahko ima več naslovov oz. podnaslovov. Na primer, vhod iz ene strani zgradbe in vhod iz druge strani zgradbe. Prav tako je zgradba na vogalu ulice obdana z več cestami.

Med bolj običajne razširitve spadajo novi domenski objekti, za vsako vrsto podatka. Če bi dodali ozke tekoče vode, ki predstavljajo reke in potoke, bi lahko naredili tako:

V GeoName bi dodali novo vrsto podatka, npr., "WATERLINE". Definirali bi nov domenski objekt:

WaterLine:

package net.tinelstudio.gis.model.domain;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Type;

import com.vividsolutions.jts.geom.LineString;

/**
 * The WaterLine entity.
 *
 * @author TineL
 */
@Entity
public class WaterLine extends NotedEntity {

  @Column(nullable=false)
  @Type(type="org.hibernatespatial.GeometryUserType")
  private LineString lineString;

  @Column
  @ManyToMany
  @Cascade(value=CascadeType.SAVE_UPDATE)
  private Set<GeoName> geoNames;

  // ...
}

Koda 6.10: Primer dodatnega domenskega objekta WaterLine. Getterji in setterji so izpuščeni.

Zahvala Kazalo Seznam uporabljenih kratic in simbolov 1 Povzetek 2 Abstract 3 Uvod 4 Opis problema 5 Načrt sistema 6 Podatkovni model 7 Geokodiranje 8 Nasprotno geokodiranje 9 Usmerjanje 10 Izkušnje in nadaljnje delo 11 Sklep Dodatek A Navodila za uporabo sistema Literatura