Spis treści

[własne] Semantic Maps - Use Case

Zespół

Opis

Wymagania

//(C) - koncept, (I) - instancja
Obiekt (C)
   | --- Beacon (C)
   |       | --- beacon1 (I)
   |       | --- beacon2 (I)
   |       | --- beacon3 (I)
   |       | --- beacon4 (I)
   |
   | --- POI (C)
           | --- krzesło (I)
           | --- stół (I)
           | --- komputer (I)
          ...

Spotkania

20141217

Spotkanie organizacyjne

Projekt

Ontologia

Aby zrealizować postawione wymagania konieczne było zmodyfikowanie ontologii SemanticMaps. Do ontologii dodano następujące DatatypeProperty identyfikujące Beacona:

<owl:DatatypeProperty rdf:about="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#hasUUID">
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
    <rdfs:domain rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#Beacon"/>
    <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
 
<owl:DatatypeProperty rdf:about="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#hasMajor">
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
    <rdfs:domain rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#Beacon"/>
    <rdfs:range rdf:resource="&xsd;positiveInteger"/>
</owl:DatatypeProperty>
 
<owl:DatatypeProperty rdf:about="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#hasMinor">
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
    <rdfs:domain rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#Beacon"/>
    <rdfs:range rdf:resource="&xsd;positiveInteger"/>
</owl:DatatypeProperty>

Dodano także następujące DatatypeProperty opisujące POI w kontekście tego projektu:

<owl:DatatypeProperty rdf:about="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#hasName">
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
    <rdfs:domain rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#POI"/>
    <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>
 
<owl:DatatypeProperty rdf:about="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#hasTag">
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
    <rdfs:domain rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#POI"/>
    <rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

Następnie zamodelowano powiązanie Beaconów z POI, zgodnie z wymaganiami stworzono relacje „w obie strony” przy pomocy następujących ObjectProperty:

<owl:ObjectProperty rdf:about="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#hasPOI">
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
    <rdfs:domain rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#Beacon"/>
    <rdfs:range rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#POI"/>
</owl:ObjectProperty>
 
<owl:ObjectProperty rdf:about="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#isAssignedToBeacon">
    <rdf:type rdf:resource="&owl;FunctionalProperty"/>
    <rdfs:domain rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#POI"/>
    <rdfs:range rdf:resource="http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#Beacon"/>
</owl:ObjectProperty>

Na koniec postanowiono stworzyć instancje. Ponieważ realizator projektu posiada tylko 2 beacony, stworzono 2 instancje klas Beacon i 2 instancje klas POI przypisane do odpowiednich beaconów:

<POI rdf:ID="POI1">
    <hasName rdf:datatype="&xsd;string">Old table</hasName>
    <hasTag rdf:datatype="&xsd;string">antique</hasTag>
    <isAssignedToBeacon rdf:resource="#Beacon1"/>
</POI>
 
<POI rdf:ID="POI2">
    <hasName rdf:datatype="&xsd;string">Old chair</hasName>
    <hasTag rdf:datatype="&xsd;string">antique</hasTag>
    <isAssignedToBeacon rdf:resource="#Beacon2"/>
</POI>
 
<Beacon rdf:ID="Beacon1">
    <hasUUID rdf:datatype="&xsd;string">F7826DA6-4FA2-4E98-8024-BC5B71E0893E</hasUUID>
    <hasMajor rdf:datatype="&xsd;positiveInteger">64903</hasMajor>
    <hasMinor rdf:datatype="&xsd;positiveInteger">35944</hasMinor>
    <hasPOI rdf:resource="#POI1"/>
</Beacon>
 
<Beacon rdf:ID="Beacon2">
    <hasUUID rdf:datatype="&xsd;string">F7826DA6-4FA2-4E98-8024-BC5B71E0893E</hasUUID>
    <hasMajor rdf:datatype="&xsd;positiveInteger">54294</hasMajor>
    <hasMinor rdf:datatype="&xsd;positiveInteger">26255</hasMinor>
    <hasPOI rdf:resource="#POI2"/>
</Beacon>

Zmodyfikowana w ten sposób ontologia znajduje się w pliku concepts.owl.zip i jest wykorzystywana na dalszych etapach projektu.

SPARQL

Aby „wyciągnąć” potrzebne informacje z ontologii wykorzystano SPARQL. Do testowania kwerend wykorzystano poznane na laboratorium narzędzie Twinkle.

Proces „wyciągania” informacji, przedstawiony w sekcji Wymagania w postaci kolejnych kroków, postanowiono podzielić na 2 etapy wraz z odpowiadającymi im kwerendami. Jako pierwszy etap przyjęto „wyciąganie” tagów z POI przypisanych do wejściowego beacona, a jako drugi „wyciąganie” danych beaconów które posiadają przypisane POI opisane „wyciągniętymi” wcześniej tagami. Przykładowa kwerenda realizująca pierwszy etap może być przedstawiona następująco:

PREFIX semmap: <http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#>

SELECT DISTINCT ?poitag
WHERE {
    ?b a semmap:Beacon.
    ?b semmap:hasUUID ?uuid.
    ?b semmap:hasMajor ?major.
    ?b semmap:hasMinor ?minor.
    FILTER (regex(?uuid, "F7826DA6-4FA2-4E98-8024-BC5B71E0893E") && ?major = 64903 && ?minor = 35944)
    ?b semmap:hasPOI ?poi.
    ?poi semmap:hasTag ?poitag.
}

Wykonanie tej kwerendy na zmodyfikowanej ontologii zwraca następujący wyniki:

--------------------------------------------------------
| poitag                                               |
========================================================
| "antique"^^<http://www.w3.org/2001/XMLSchema#string> |
--------------------------------------------------------

Jest to wynik zgodny z oczekiwaniami i zgodny z przedstawioną modyfikacją ontologii. W drugim etapie zwrócone tagi są wykorzystywane do znalezienia beaconów zawierających POI opisane tymi tagami. Przykładowa kwerenda realizująca ten etap dla zwróconego tagu przedstawionego powyżej może być przedstawiona następująco:

PREFIX semmap: <http://www.semanticweb.org/agh/wshop/semanticmaps/Concepts#>

SELECT DISTINCT ?uuid ?major ?minor
WHERE {
    ?p a semmap:POI.
    ?p semmap:hasName ?poiname.
    ?p semmap:hasTag ?poitag
    FILTER (regex(?poitag, "antique"))
    ?p semmap:isAssignedToBeacon ?b.
    ?b semmap:hasUUID ?uuid.
    ?b semmap:hasMajor ?major.
    ?b semmap:hasMinor ?minor.
}

Wykonanie tej kwerendy na zmodyfikowanej ontologii zwraca następujący wyniki:

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| uuid                                                                              | major                                                       | minor                                                       |
=================================================================================================================================================================================================================
| "F7826DA6-4FA2-4E98-8024-BC5B71E0893E"^^<http://www.w3.org/2001/XMLSchema#string> | "54294"^^<http://www.w3.org/2001/XMLSchema#positiveInteger> | "26255"^^<http://www.w3.org/2001/XMLSchema#positiveInteger> |
| "F7826DA6-4FA2-4E98-8024-BC5B71E0893E"^^<http://www.w3.org/2001/XMLSchema#string> | "64903"^^<http://www.w3.org/2001/XMLSchema#positiveInteger> | "35944"^^<http://www.w3.org/2001/XMLSchema#positiveInteger> |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Wynik również jest zgodny z oczekiwaniami i zgodny z przedstawioną modyfikacją ontologii. Jak widać, zwrócony został także wejściowy beacon, nie jest jednak problemem odrzucenie go w aplikacji wykorzystującej to rozwiązanie.

Dzięki przedstawionemu zestawowi dwóch kwerend SPARQL zrealizowane są wszystkie przedstawione w sekcji Wymagania kroki propozycji wnioskowania poza ostatnim.

Fuseki

Zgodnie z ustaleniami ze spotkania organizacyjnego praktyczne rozwiązanie postanowiono zaprojektować zgodnie z architekturą klient-serwer, z „wiedzą” w postaci ontologii dostępną na serwerze oraz aplikacją mobilną wysyłającą zapytania do tego serwera. Jako serwer wybrano Apache Jena Fuseki, wspierający RESTowy SPARQL.

Integracja wypracowanego do tego etapu rozwiązania w postaci zamieszczonej ontologii z serwerem okazała się być trywialna i sprowadziła się do załadowania pliku owl przez Control Panel serwera w przeglądarce. Interfejs webowy serwera udostępnia także możliwość testowania kwerend SPARQL i zwracania ich w różnych formatach. Aby zweryfikować poprawną integrację postanowiono przetestować zamieszczone kwerendy na serwerze Fuseki.

Import zamieszczonej ontologii do Fuseki przebiegł pomyślnie:

Wykonanie pierwszej kwerendy

zwróciło następujący wynik (przedstawiony w JSON gdyż w ten sposób będzie realizowana komunikacja z klientem):

{
  "head": {
    "vars": [ "poitag" ]
  } ,
  "results": {
    "bindings": [
      {
        "poitag": { "datatype": "http://www.w3.org/2001/XMLSchema#string" , "type": "typed-literal" , "value": "antique" }
      }
    ]
  }
}

Jest to wynik zgodny z wynikiem otrzymanym z Twinkle. Wykonanie drugiej kwerendy

zwróciło następujący wynik:

{
  "head": {
    "vars": [ "uuid" , "major" , "minor" ]
  } ,
  "results": {
    "bindings": [
      {
        "uuid": { "datatype": "http://www.w3.org/2001/XMLSchema#string" , "type": "typed-literal" , "value": "F7826DA6-4FA2-4E98-8024-BC5B71E0893E" } ,
        "major": { "datatype": "http://www.w3.org/2001/XMLSchema#positiveInteger" , "type": "typed-literal" , "value": "64903" } ,
        "minor": { "datatype": "http://www.w3.org/2001/XMLSchema#positiveInteger" , "type": "typed-literal" , "value": "35944" }
      } ,
      {
        "uuid": { "datatype": "http://www.w3.org/2001/XMLSchema#string" , "type": "typed-literal" , "value": "F7826DA6-4FA2-4E98-8024-BC5B71E0893E" } ,
        "major": { "datatype": "http://www.w3.org/2001/XMLSchema#positiveInteger" , "type": "typed-literal" , "value": "54294" } ,
        "minor": { "datatype": "http://www.w3.org/2001/XMLSchema#positiveInteger" , "type": "typed-literal" , "value": "26255" }
      }
    ]
  }
}

Jest to wynik również zgodny z wynikiem otrzymanym z Twinkle. Dzięki temu ontologia zyskała dostęp w postaci SPARQL przez HTTP.

Aplikacja mobilna

Aplikacja mobilna komunikuje się z serwerem Fuseki za pomocą zamieszczonych kwerend SPARQL, uzupełnianych o rzeczywiste dane, przekazywanych jako parametry w query string. Kwerendy budowane są za pomocą biblioteki sparql-java, danymi uzupełnianymi są w przypadku pierwszej kwerendy uuid, major i minor rzeczywistego beacona, a przypadku drugiej kwerendy tag.

Beacon wejściowym jest wybierany z listy dostępnych beaconów na podstawie RSSI - beacon o największej sile sygnału jest uznawany za najbliższy i podawany na wejście algorytmu. Odpowiedź serwera w postaci JSONa jest odczytywana ze strumienia i parsowana za pomocą klas z pakietu org.json dostępnego w API Androida. Na podstawie przeparsowanej odpowiedzi tworzona jest lista „podobnych” beaconów.

W warstwie prezentacji aplikacji najważniejszym elementem jest mapa. Mapy tworzone i wyświetlane są przy pomocy biblioteki micromap. Wstępnie beacon najbliższy jest oznaczany na mapie większą, wyboldowaną czcionką a beacony „podobne” tą samą, większą czcionką ale nie wyboldowaną.

Prosty scenariusz wykorzystania rozwiązania można zobaczyć na nagraniu z aplikacji - semmap_usecase1.zip. Wyświetlona mapka przedstawia pokój realizatora projektu, eksperyment był wykonywany w pobliżu lewego górnego rogu mapki. Na początku nagrania wyświetlone są dane beaconów (uuid, major i minor), które można porównać z zamieszczoną ontologią. Następnie wykorzystana jest funkcja znajdowania najbliższego beacona, beacon znajdujący się w górze mapki zostaje poprawnie wybrany jako najbliższy i zaznaczony większą, wyboldowaną czcionką. Na koniec wykorzystana jest funkcja znajdowania beaconów „podobnych” - pierwsza kwerenda zostaje uzupełniona danymi najbliższego beacona, następuje komunikacja z serwerem, zwrócona zostaje jednoelementowa lista tagów zawierająca element „antique”, druga kwerenda zostaje uzupełniona tagiem „antique”, następuje komunikacja z serwerem, zwrócona zostaje dwuelementowa lista beaconów z której usunięty zostaje beacon wejściowy, pozostały beacon zostaje zaznaczony większą, niewyboldowaną czcionką.

Aktualnie zaimplementowany i przedstawiony na nagraniu sposób wykorzystania rozwiązania i wyświetlania beaconów na mapie jest jedynie wstępną propozycją, przydatną do zaprezentowania działania rozwiązania. Jest to jednak sposób mało praktyczny i mało atrakcyjny wizualnie, w ramach propozycji dalszych prac zaproponować można usprawnienie warstwy prezentacyjnej i UX rozwiązania.

Sprawozdanie

Projekt Gradle pluginu aware-beacons-v2 (branch semweb) - aware-beacons-semweb.zip

Materiały