Status of This Document
This document is an incomplete draft.You are invited to contribute any feedback, comments, or questions you might have.
1. Introduction
The VIADE data model contains a specification for a common data model to represent routes in RDF inside Solid data Pods. The data model has been prepared by the teachers and students of the Software Architecture course as part of the 2019-20 edition. The assignment description consists in the implementation of Solid based apps for route management. In order to improve the interoperability between those apps, a common data model is required and this document is an attempt to provide one.
1.1. Definitions
A data pod is a place for storing documents, with mechanisms for controlling who can access what.
A Solid app is an application that reads or writes data from one or more data pods.
1.2. Namespaces
Prefix | Namespace | Description |
---|---|---|
gpx | https://www.w3.org/ns/pim/gpx# | [GPX] |
ldp | http://www.w3.org/ns/ldp# | [LDP] |
schema | http://schema.org/ | Schema.org |
rdf | http://www.w3.org/1999/02/22-rdf-syntax-ns# | [rdf11-concepts] |
rdfs | http://www.w3.org/2000/01/rdf-schema# | [rdf-schema] |
solid | http://www.w3.org/ns/solid/terms# | Solid Terms |
viade | http://arquisoft.github.io/viadeSpec/ | Viade specification |
1.3. Data model
Note: This data model is a proposal made by the students and teachers of the Software Architecture Course (edition 2019/20) at the University of Oviedo. The data model is work-in-progress and we appreciate any feedback which can be done by raising issues.
A route must contain a name, optionally it can have a description. Its composed by a list of points which must conform be GeoCoordinates and may or not have a list of comments and media elements.
The GeoCoordinates must have latitude and longitude. They can have as optional properties a name, altitude and a physical address.
A UserComment must contain the text of the comment, the time it was published and the author, being the last one an IRI to a pod profile shape.
And finally, an Image or a Video must have an IRI to be a resource, another IRI to the pod profile of the author and the time it was published.
prefix : <http://arquisoft.github.io/viadeSpec/> prefix schema: <http://schema.org/> prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> # Represents a route : Route { schema: name xsd: string ; schema: description xsd: string ?; : point @: Point *; : gpx @: GPXShape ; // See ShEx description for GPX Routes: hasComments xsd: string OR @: UserComment *; : hasMediaAttached @: Image OR @: Video *; } : Point @: GeoCoordinates AND{ : order xsd: integer ; } # Represents geo coordinates following: https://schema.org/GeoCoordinates : GeoCoordinates { schema: latitude xsd: decimal ; schema: longitude xsd: decimal ; schema: elevation xsd: decimal ?; } : UserComment IRI{ schema: text xsd: string ; schema: publishedDate xsd: datetime ; schema: author @: PodProfile ; } : Image { schema: contentUrl IRI; schema: publishedDate xsd: datetime ; schema: author @: PodProfile ; } : Video { schema: contentUrl IRI; schema: publishedDate xsd: dateTime ; schema: author @: PodProfile ; } : PodProfile IRI# This model is work-in progress : GPXShape {} // See GPX section
1.3.1. Simple routes examples
An example of a route in Turtle following the previous Shape could be:
prefix viade: <http://arquisoft.github.io/viadeSpec/> prefix : <http://example.org/> prefix schema: <http://schema.org/> prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> : myRoute a viade: Route ; schema: name "My first route" ; schema: description "This is an example of route" ; viade: point [ schema: latitude 47.64458 ; schema: longitude -122.326897 ; viade: order 1 ] ; viade: point [ schema: latitude 47.644532 ; schema: longitude -123.3345 ; viade: order 2 ; schema: elevation 34 ]; viade: hasComments "I really enjoyed this route" ; viade: hasMediaAttached : media1 . : media1 schema: contentUrl <http://example.org/picture.jpg> ; schema: publishedDate "2020-03-26T21:32:52" ^^ xsd:dateTime; schema: author <https://labra.solid.community/profile/card#me> .
You can run the example using the RDFShape tool
A possible SPARQL query to retrieve the route information and it’s points can be:
PREFIX schema: <http://schema.org/> PREFIX viade: <http://arquisoft.github.io/viadeSpec/> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> SELECT ?lat ?long ?order ?name ?description ?name ?elevation WHERE{ ?routea viade: Route . ?routeviade: point ?point. ?pointschema: latitude ?lat; schema: longitude ?long; viade: order ?order. OPTIONAL{ ?routeschema: description ?description.} OPTIONAL{ ?routeschema: name ?name.} OPTIONAL{ ?pointschema: elevation ?elevation.} }
1.4. GPX Routes
[GPX] is a popular XML format that could be used to represent routes. There are two possibilities: to convert the XML format to Turtle or to embed the XML file as an RDF literal.
1.4.1. GPX Routes converted to Turtle
GPX routes can be represented in Turtle using the following Shape Expression:
prefix : <https://www.w3.org/ns/pim/gpx#> prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> prefix gpx: <https://www.w3.org/ns/pim/gpx#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> # This document contains a ShEx representation of the XML Schema defined for GPX # The original schema URI is <http://www.topografix.com/GPX/1/1> start = @: gpxType # GPX documents contain a metadata header, followed by waypoints, routes, and tracks. # You can add your own elements to the extensions section of the GPX document : gpxType { gpx: creator xsd: string ?; gpx: version xsd: string ?; gpx: metadata @: metadataType ?; gpx: wpt @: wptType *; gpx: rte @: rteType *; gpx: trk @: trkType *; gpx: extensions @: extensionsType ?} # Information about the GPX file, author, and copyright restrictions goes in the metadata section. # Providing rich, meaningful information about your GPX files allows others to search for and use your GPS data. : metadataType { gpx: name xsd: string ?; gpx: desc xsd: string ?; gpx: author @: personType ?; gpx: copyright @: copyrightType ?; gpx: link @: linkType *; gpx: time xsd: dateTime ?; gpx: keywords xsd: string ?; gpx: bounds @: boundsType ?; gpx: extensions @: extensionsType ?} # wpt represents a waypoint, point of interest, or named feature on a map : wptType { gpx: lat @: latitudeType ; gpx: lon @: longitudeType ; gpx: ele xsd: decimal ?; gpx: time xsd: dateTime ?; gpx: magvar @: degreesType ?; gpx: geoidheight xsd: decimal ?; gpx: name xsd: string ?; gpx: cmt xsd: string ?; gpx: desc xsd: string ?; gpx: src xsd: string ?; gpx: link @: linkType ?; gpx: sym xsd: string ?; gpx: type xsd: string ?; gpx: fix @: fixType ?; gpx: sat xsd: nonNegativeInteger ?; gpx: hdop xsd: decimal ?; gpx: vdop xsd: decimal ?; gpx: pdop xsd: decimal ?; gpx: ageofdgpsdata xsd: decimal ?; gpx: dgpsid @: dgpsStationType ?; gpx: extensions @: extensionsType ?} # rte represents route - an ordered list of waypoints representing a series of turn points leading to a destination : rteType { gpx: name xsd: string ?; gpx: cmt xsd: string ?; gpx: desc xsd: string ?; gpx: src xsd: string ?; gpx: link @: linkType *; gpx: number xsd: nonNegativeInteger ?; gpx: type xsd: string ?; gpx: extensions @: extensionsType ?; gpx: rtept @: wptTypeList } : wptTypeList [ rdf: nil ] OR CLOSED{ rdf: first { gpx: trkpt @: wptType } ; rdf: rest @: wptTypeList } # trk represents a track - an ordered list of points describing a path : trkType { gpx: name xsd: string ?; gpx: cmt xsd: string ?; gpx: desc xsd: string ?; gpx: src xsd: string ?; gpx: link @: linkType *; gpx: number xsd: nonNegativeInteger ?; gpx: type xsd: string ?; gpx: extensions @: extensionsType ?; gpx: trkseg @: trksegType *} # Allow any elements from a namespace other than this schema’s namespace : extensionsType { } # A Track Segment holds a list of Track Points which are logically connected in order. # To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, # start a new Track Segment for each continuous span of track data. : trksegType { gpx: trkpts @: wptTypeList *; gpx: extensions @: extensionsType ?} # Information about the copyright holder and any license governing use of this file. # By linking to an appropriate license, you may place your data into the public domain or grant additional usage rights. : copyrightType { gpx: author xsd: string ; gpx: year xsd: gYear ?; gpx: license xsd: anyURI ?} # A link to an external resource (Web page, digital photo, video clip, etc) with additional information : linkType { gpx: href xsd: anyURI ; gpx: text xsd: string ?; gpx: type xsd: string ?} # An email address. Broken into two parts (id and domain) to help prevent email harvesting. : emailType { gpx: id xsd: string ; gpx: domain xsd: string ; } # A person or organization. : personType { gpx: name xsd: string ?; gpx: : emailType ; gpx: link @: linkType } # A geographic point with optional elevation and time. Available for use by other schemas. : ptType { gpx: lat @: latitudeType ; gpx: long @: longitudeType ; gpx: ele xsd: decimal ?; gpx: time xsd: dateTime ?} # An ordered sequence of points. (for polygons or polylines, e.g.) : ptsegType [ rdf: nil ] OR CLOSED{ rdf: first { gpx: pt @: ptType } ; rdf: rest @: ptsegType ; } # Two lat/lon pairs defining the extent of an element. : boundsType { gpx: minlat @: latitudeType ; gpx: minlon @: longitudeType ; gpx: maxlat @: latitudeType ; gpx: maxlon @: longitudeType } # The latitude of the point. Decimal degrees, WGS84 datum : latitudeType xsd: decimal minInclusive-90.0 maxInclusive90.0 # The longitude of the point. Decimal degrees, WGS84 datum. : longitudeType xsd: decimal minInclusive-180.0 maxInclusive180.0 # Used for bearing, heading, course. Units are decimal degrees, true (not magnetic). : degreesType xsd: decimal minInclusive0.0 maxInclusive360.0 # Type of GPS fix. none means GPS had no fix. To signify "the fix info is unknown, leave out fixType entirely. pps = military signal used : fixType [ "none" "2d" "3d" "dgps" "pps" ] # Represents a differential GPS station. : dgpsStationType xsd: integer minInclusive0 maxInclusive1023
1.4.2. GPX Route example as Turtle
As an example a VIADE route represented using GPX as Turtle could be:
prefix : <http://example.org/> prefix viade: <http://arquisoft.github.io/viadeSpec/> prefix schema: <http://schema.org/> prefix xsd: <http://www.w3.org/2001/XMLSchema#> prefix gpx: <https://www.w3.org/ns/pim/gpx#> : exampleRoute a viade: Route ; schema: name "A nice walk" ; viade: gpx [ gpx: creator "Oregon 400t" ; gpx: version "1.1" ; gpx: metadata [ gpx: link [ gpx: href "http://www.garmin.com" ; gpx: text "Garmin International" ] ; gpx: time "2009-10-17T22:58:43Z" ^^ xsd:dateTime] ; gpx: trk [ gpx: name "Example GPX Document" ; gpx: trkseg [ gpx: trkpts ( [ gpx: trkpt [ gpx: ele 4.46 ; gpx: lat 47.644548 ; gpx: lon -122.326897 ; gpx: time "2009-10-17T18:37:26Z" ^^ xsd:dateTime] ] [ gpx: trkpt [ gpx: ele 4.94 ; gpx: lat 47.644548 ; gpx: lon -122.326897 ; gpx: time "2009-10-17T18:37:31Z" ^^ xsd:dateTime] ] [ gpx: trkpt [ gpx: ele 6.87 ; gpx: lat 47.644548 ; gpx: lon -122.326897 ; gpx: time "2009-10-17T18:37:34Z" ^^ xsd:dateTime] ] ) ] ] ] .
1.4.3. GPX routes embedded as literals
It is also possible to represent the routes embedding the XML content as an RDF literal.
The following example contains a GPX route embedded as an RDF literal.
prefix : <http://example.org/> prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> prefix viade: <http://arquisoft.github.io/viadeSpec/> prefix schema: <http://schema.org/> prefix xsd: <http://www.w3.org/2001/XMLSchema#> prefix gpx: <https://www.w3.org/ns/pim/gpx#> : exampleRoute a viade: Route ; schema: name "A nice walk" ; viade: gpx [ viade: source """<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" creator="Oregon 400t" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd"> <metadata> <link href="http://www.garmin.com"> <text>Garmin International</text> </link> <time>2009-10-17T22:58:43Z</time> </metadata> <trk> <name>Example GPX Document</name> <trkseg> <trkpt lat="47.644548" lon="-122.326897"> <ele>4.46</ele> <time>2009-10-17T18:37:26Z</time> </trkpt> <trkpt lat="47.644548" lon="-122.326897"> <ele>4.94</ele> <time>2009-10-17T18:37:31Z</time> </trkpt> <trkpt lat="47.644548" lon="-122.326897"> <ele>6.87</ele> <time>2009-10-17T18:37:34Z</time> </trkpt> </trkseg> </trk> </gpx>"""^^rdf:XMLLiteral ; ] .
1.4.4. GPX to RDF converters
It is possible to convert from GPX files in XML to RDF files in RDF/XML using the following XSLT file
2. Pod Management
It is crucial for the interoperability that we handle the POD data in the same way. Here is a proposal of what we can do. Note that this pretends to be an starting point to the complete specification, and so it is open to discussion.
2.1. File format
For the file format we can use JSON-LD, we have used JSON along the degree and we are very familiar with it, it won’t be a problem to handle that in the application, in fact Javascript facilitates that task with the built-in object "JSON".2.1.1. File example
What comes next is an example of the minimum data that should be stored in the pod with the JSON-LD. More data could be added by our apps, but at least this should be there.The following properties can be found:
-
name: the name of the route.
-
author: the webId of the creator of the route (optional).
-
description: a description of the route (optional).
-
waypoints: a list of waypoints (optional).
-
points: points of the route, describing the path.
-
comments: a link (@id) to a file related with the route where the users comments will be stored.
-
date: the date of creation of the route (optional).
-
media: a list of links to resources. Also contains the optional property "name" for each media element.
The example (if any error is seen in the context, please point it out):
_If it cannot be seen in the include please check it manually because afterwards there will be mentions about it._
{ "@context": { "@version": 1.1, "comments": { "@id": "viade:comments", "@type": "@id" }, "description": { "@id": "schema:description", "@type": "xsd:string" }, "media": { "@container": "@list", "@id": "viade:media" }, "name": { "@id": "schema:name", "@type": "xsd:string" }, "points": { "@container": "@list", "@id": "viade:points" }, "latitude": { "@id": "schema:latitude", "@type": "xsd:double" }, "longitude": { "@id": "schema:longitude", "@type": "xsd:double" }, "elevation": { "@id": "schema:elevation", "@type": "xsd:double" }, "author":{ "@id": "schema:author", "@type": "@id" }, "date": { "@id": "schema:DateTime", "@type": "xsd:dateTime" }, "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdfs": "http://www.w3.org/2000/01/rdf-schema#", "schema": "http://schema.org/", "viade": "http://arquisoft.github.io/viadeSpec/", "xsd": "http://www.w3.org/2001/XMLSchema#" }, "name": "Route test 1", "author": "https://luispresacollada.solid.community/profile/card#me", "description": "This is a test to see the output of the JsonLDConversor", "comments": "http://inrupt.luispc1998/viade/comments/commentExample.json", "date": "2020-02-25T18:50:22Z", "media": [ { "@id": "http://inrupt.luispc1998/viade/resources/da34fas749sa3h883j.jpg", "name": "MyFoto" }, { "@id": "http://inrupt.angelixus/viade/resources/pt92as74234a3h5xb3j.mp4", "name": "MyVideo" }, { "@id": "http://inrupt.raupemol/viade/resources/da34zas4213sa7b542.png", "name": "OtherFoto" }, { "@id": "http://inrupt.luispc1998/viade/resources/da345432jtsa7b542e.mp4", "name": "OtherVideo" } ], "waypoints" : [ { "name": "Name for the waypoint", "description": "Description of the waypoint", "latitude": 45.123, "longitude": 34.121, "elevation": 34 }, { "name": "Computer Science School", "description": "Become a good engineer with us", "latitude": 45.123, "longitude": 34.121, "elevation": 34 }
], "points": [ { "latitude": 45.123, "longitude": 34.121, "elevation": 34 }, { "latitude": 46.123, "longitude": 34.121, "elevation": 36 }, { "latitude": 47.123, "longitude": 34, "elevation": 39 }, { "latitude": 48.123, "longitude": 32.121, "elevation": 40 }, { "latitude": 49.123, "longitude": 34.121, "elevation": 43 }, { "latitude": 40.123, "longitude": 32.121, "elevation": 46 }, { "latitude": 50.123, "longitude": 33.121, "elevation": 50 }, { "latitude": 53.123, "longitude": 34.121, "elevation": 55 }, { "latitude": 54.123, "longitude": 34.121, "elevation": 56 }, { "latitude": 55.123, "longitude": 35.121, "elevation": 50 }, { "latitude": 55.123, "longitude": 34.121, "elevation": 45 } ]
}2.2. Directory hierarchy in the POD
Not only we need to state what information should be in the pod, but also where it is located.We proposed a "viade" folder in the root of the pod, and inside it three folders: routes, comments, resources.
viade/ routes/ example.jsonld example2.jsonld comments/ ruta1Comments.jsonld ruta2Comments.jsonld resources/ foto.jpg image.png video.mp4 inbox/ sharingNotification1.jsonld sharingNotification2.jsonld shared/ luispresacollada.solid.community.jsonld
2.2.1. Comments and resources
Each route will have linked a file for the comments from the very momment of its creation. In this file the users that have received the route in a share operation can post their comments. This approach is highly efficient and by using it we get rid of permissions problems with respect to the one that stores the comments of each user in his pod.
Such files just to follow a convention are supposed to be stored in viade/comments folder, although since we are navigating through a graph it is not really important from the interoperability point of view where is the file.
Consider the following example:
Luis published a route long ago, and now he shared it with his friend Sara. Sara wants to comment it. Sara will access the comments file of the route by means of the route file, and she will post there her comment. She has permissions because Luis gave them to her when sharing the route.
You can find examples of the files int the jsonlfProposal folder.
2.3. What should be done about GPX?
A gpx file can contain several elements "trk" (from the word "track") which are the ones that match our concept of "Route", that is a path, a sequence of points. Inside this label we can have several "trkseg", which stand as sequences of ordered points (trkpt). Our duty will be to take all the trkpt and put them inside the "viade:points" property of the example json.The gpx trk label can have a name and a description so it could be direct translation. Or we may want the user to introduce that data, that’s an app design decision.
There is a type of points in a GPX called "waypoints", they are relevant points that are in some way related to the specified tracks. Assuming a path across a city, a waypoint can be a church, a restaurant, a monument, and so far so forth. We will include this points in our routes jsonld files. They will have name, description and coordinates. Check the provided example to see its implementation. Elevation and description are optional attributes.
2.4. How to share
We have performed a voting in order to follow the approach of the permissions, or use the inbox folder in order to manipulate the sharing functionality. In the end we wil be using the second one. But for such a task we need some kind of agreement in the file structure in the "receiver". Assume Luis wants to share a route with Sara. Luis will give her permissions to the files, and send her a notification, with the webid of the route and his own webid. Sara’s app will parse the notification and add that route id to a file.Such files will be placed in the folder viade/shared, and will have a name such as: luispresacollada.solid.community. (The corresponding part of the webID) You may check the structure in the file "sharedRoutesExample", it contains a simple list with @id pointing to the shared routes.
{ "@context": { "@version": 1.1, "routes": { "@container": "@list", "@id": "viade:routes" }, "viade": "http://arquisoft.github.io/viadeSpec/" }, "routes": [ { "@id": "http://inrupt.luispc1998/viade/routes/route1.jsonld" }, { "@id": "http://inrupt.angelixus/viade/routes/example.jsonld" }, { "@id": "http://inrupt.raupemol/viade/routes/magicalRoute.jsonld" }, { "@id": "http://inrupt.luispc1998/viade/routes/exampl2.jsonld" } ]
}