User:Esscue/OsmarenderOWL

From OpenStreetMap Wiki
Jump to navigation Jump to search

Devoloping my label placement optimizer I noticed that the SVG maps created with Osmarender lack some information that would be really useful. For example it is only possible to match labels by comparing coordinates to the features they describe or which area (or closed way) they belong to. Semantical information making the map accessible for a computer application would therefore be nice. Not only my application would profit from this information. These information could be used in a lot of contexts, here are some ideas:

  • applications displaying the svg map and using the information for highlighting point features or adding and calculating routes within the map
  • semantic web search engines which index the svg maps and gives the user the ability to search for a map showing a special OSM node or way

So you see it sounds really nice, but how to do this?

A good friend of mine writing his diploma thesis about a topic in the area of semantic web gave me an initial hint with RDF (Resource Description Language). Doing some research on my own I came to OWL (Web Ontology Language) which seems to be the defacto standard for describing semantics. A concept called inference is also a plus for OWL. That means that a program is able to extract information from a OWL description that is not explicitly contained. There are a lot of tools for working with RDF/OWL:

As you can see it is possible with OWL to describe the map, but how to connect the features drawn on the map to their original OSM data?

Fortunately I found a young project, called linkedgeodata (http://linkedgeodata.org/About) publishing a RDF description of the whole OSM data. This RDF description of the OSM data will play a central role in my OWL description of the SVG maps.

OWL Ontology

Here is my first draft of an OWL ontology called svgmap for describing SVG maps created with Osmarender (Download):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rdf:RDF [
  <!ENTITY owl "http://www.w3.org/2002/07/owl#">
  <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#">
  <!ENTITY svgmap "http://www.openstreetmap.org/osmarender/ontology/svgmap/01/svgmap">
  <!ENTITY vocabulary "http://linkedgeodata.org/vocabulary">
  <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#">
]>
<rdf:RDF xml:base="&svgmap;"
         xmlns:owl="&owl;"
         xmlns:rdf="&rdf;"
         xmlns:rdfs="&rdfs;">

<!-- Ontology Information -->
  <owl:Ontology rdf:about="">
    <owl:imports>
      <owl:Ontology rdf:about="&vocabulary;"/>
    </owl:imports>
  </owl:Ontology>

<!-- Classes -->
  <owl:Class rdf:about="#AreaFeature">
    <rdfs:subClassOf rdf:resource="#Feature"/>
  </owl:Class>

  <owl:Class rdf:about="#AreaFeatureLabel">
    <rdfs:subClassOf rdf:resource="#Label"/>
  </owl:Class>

  <owl:Class rdf:about="#Feature"/>
  <owl:Class rdf:about="#Label"/>
  <owl:Class rdf:about="#LineFeature">
    <rdfs:subClassOf rdf:resource="#Feature"/>
  </owl:Class>

  <owl:Class rdf:about="#LineFeatureLabel">
    <rdfs:subClassOf rdf:resource="#Label"/>
  </owl:Class>

  <owl:Class rdf:about="#OSMArea">
    <rdfs:subClassOf rdf:resource="#OSMData"/>
    <owl:equivalentClass rdf:resource="http://linkedgeodata.org/triplify/way"/>
  </owl:Class>

  <owl:Class rdf:about="#OSMData"/>
  <owl:Class rdf:about="#OSMNode">
    <rdfs:subClassOf rdf:resource="#OSMData"/>
    <owl:equivalentClass rdf:resource="http://linkedgeodata.org/triplify/node"/>
  </owl:Class>

  <owl:Class rdf:about="#OSMWay">
    <rdfs:subClassOf rdf:resource="#OSMData"/>
    <owl:equivalentClass rdf:resource="http://linkedgeodata.org/triplify/way"/>
  </owl:Class>

  <owl:Class rdf:about="#PointFeature">
    <rdfs:subClassOf rdf:resource="#Feature"/>
  </owl:Class>

  <owl:Class rdf:about="#PointFeatureLabel">
    <rdfs:subClassOf rdf:resource="#Label"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGCircleElement">
    <rdfs:subClassOf rdf:resource="#SVGElement"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGElement">
    <rdfs:subClassOf rdf:resource="#XMLElement"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGGElement">
    <rdfs:subClassOf rdf:resource="#SVGElement"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGMap"/>
  <owl:Class rdf:about="#SVGPathElement">
    <rdfs:subClassOf rdf:resource="#SVGElement"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGSVGElement">
    <rdfs:subClassOf rdf:resource="#SVGElement"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGTextElement">
    <rdfs:subClassOf rdf:resource="#SVGElement"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGTextPathElement">
    <rdfs:subClassOf rdf:resource="#SVGElement"/>
  </owl:Class>

  <owl:Class rdf:about="#SVGUseElement">
    <rdfs:subClassOf rdf:resource="#SVGElement"/>
  </owl:Class>

  <owl:Class rdf:about="#XMLElement"/>
  <owl:Class rdf:about="http://linkedgeodata.org/triplify/node"/>
  <owl:Class rdf:about="http://linkedgeodata.org/triplify/way"/>
  <owl:Class rdf:nodeID="b19">
    <owl:unionOf rdf:parseType="Collection">
      <rdf:Description rdf:about="#SVGCircleElement"/>
      <rdf:Description rdf:about="#SVGGElement"/>
    </owl:unionOf>
  </owl:Class>

  <owl:Class rdf:nodeID="b18">
    <owl:unionOf rdf:parseType="Collection">
      <rdf:Description rdf:about="#SVGCircleElement"/>
      <rdf:Description rdf:about="#SVGPathElement"/>
    </owl:unionOf>
  </owl:Class>

<!-- Object Properties -->
  <owl:ObjectProperty rdf:about="#areaFeatureDrawnBy">
    <rdfs:domain rdf:resource="#AreaFeature"/>
    <rdfs:range rdf:nodeID="b18"/>
    <rdfs:subPropertyOf rdf:resource="#drawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#areaFeatureLabelDrawnBy">
    <rdfs:domain rdf:resource="#AreaFeatureLabel"/>
    <rdfs:range rdf:resource="#SVGTextElement"/>
    <rdfs:subPropertyOf rdf:resource="#labelDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#areaFeatureShownBy">
    <rdfs:subPropertyOf rdf:resource="#shownBy"/>
    <owl:inverseOf rdf:resource="#showsAreaFeature"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#areaFeatureStandsFor">
    <rdfs:domain rdf:resource="#AreaFeature"/>
    <rdfs:range rdf:resource="#OSMArea"/>
    <rdfs:subPropertyOf rdf:resource="#standsFor"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawnBy">
    <rdfs:domain rdf:resource="#Feature"/>
    <rdfs:range rdf:resource="#SVGElement"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#draws">
    <owl:inverseOf rdf:resource="#drawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawsAreaFeature">
    <rdfs:subPropertyOf rdf:resource="#draws"/>
    <owl:inverseOf rdf:resource="#areaFeatureDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawsAreaFeatureLabel">
    <rdfs:subPropertyOf rdf:resource="#drawsLabel"/>
    <owl:inverseOf rdf:resource="#areaFeatureLabelDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawsLabel">
    <owl:inverseOf rdf:resource="#labelDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawsLineFeature">
    <rdfs:subPropertyOf rdf:resource="#draws"/>
    <owl:inverseOf rdf:resource="#lineFeatureDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawsLineFeatureLabel">
    <rdfs:subPropertyOf rdf:resource="#drawsLabel"/>
    <owl:inverseOf rdf:resource="#lineFeatureLabelDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawsPointFeature">
    <rdfs:subPropertyOf rdf:resource="#draws"/>
    <owl:inverseOf rdf:resource="#pointFeatureDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#drawsPointFeatureLabel">
    <rdfs:subPropertyOf rdf:resource="#drawsLabel"/>
    <owl:inverseOf rdf:resource="#pointFeatureLabelDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#hasAreaFeatureLabel">
    <rdfs:domain rdf:resource="#AreaFeature"/>
    <rdfs:range rdf:resource="#AreaFeatureLabel"/>
    <rdfs:subPropertyOf rdf:resource="#hasLabel"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#hasLabel">
    <rdfs:domain rdf:resource="#Feature"/>
    <rdfs:range rdf:resource="#Label"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#hasLineFeatureLabel">
    <rdfs:domain rdf:resource="#LineFeature"/>
    <rdfs:range rdf:resource="#LineFeatureLabel"/>
    <rdfs:subPropertyOf rdf:resource="#hasLabel"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#hasNode">
    <rdfs:domain rdf:resource="#OSMWay"/>
    <rdfs:range rdf:resource="#OSMNode"/>
    <owl:equivalentProperty rdf:resource="http://linkedgeodata.org/triplify/hasNode"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#hasPointFeatureLabel">
    <rdfs:domain rdf:resource="#PointFeature"/>
    <rdfs:range rdf:resource="#PointFeatureLabel"/>
    <rdfs:subPropertyOf rdf:resource="#hasLabel"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#isAreaFeatureLabel">
    <rdfs:subPropertyOf rdf:resource="#isLabel"/>
    <owl:inverseOf rdf:resource="#hasAreaFeatureLabel"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#isLabel">
    <owl:inverseOf rdf:resource="#hasLabel"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#isLineFeatureLabel">
    <rdfs:subPropertyOf rdf:resource="#isLabel"/>
    <owl:inverseOf rdf:resource="#hasLineFeatureLabel"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#isNodeOf">
    <owl:inverseOf rdf:resource="#hasNode"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#isPointFeatureLabel">
    <rdfs:subPropertyOf rdf:resource="#isLabel"/>
    <owl:inverseOf rdf:resource="#hasPointFeatureLabel"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#labelDrawnBy">
    <rdfs:domain rdf:resource="#Label"/>
    <rdfs:range rdf:resource="#SVGElement"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#lineFeatureDrawnBy">
    <rdfs:domain rdf:resource="#LineFeature"/>
    <rdfs:range rdf:resource="#SVGUseElement"/>
    <rdfs:subPropertyOf rdf:resource="#drawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#lineFeatureLabelDrawnBy">
    <rdfs:domain rdf:resource="#LineFeatureLabel"/>
    <rdfs:range rdf:resource="#SVGTextElement"/>
    <rdfs:subPropertyOf rdf:resource="#labelDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#lineFeatureShownBy">
    <rdfs:subPropertyOf rdf:resource="#shownBy"/>
    <owl:inverseOf rdf:resource="#showsLineFeature"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#lineFeatureStandsFor">
    <rdfs:domain rdf:resource="#LineFeature"/>
    <rdfs:range rdf:resource="#OSMWay"/>
    <rdfs:subPropertyOf rdf:resource="#standsFor"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#pointFeatureDrawnBy">
    <rdfs:domain rdf:resource="#PointFeature"/>
    <rdfs:range rdf:nodeID="b19"/>
    <rdfs:subPropertyOf rdf:resource="#drawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#pointFeatureLabelDrawnBy">
    <rdfs:domain rdf:resource="#PointFeatureLabel"/>
    <rdfs:range rdf:resource="#SVGTextElement"/>
    <rdfs:subPropertyOf rdf:resource="#labelDrawnBy"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#pointFeatureShownBy">
    <rdfs:subPropertyOf rdf:resource="#shownBy"/>
    <owl:inverseOf rdf:resource="#showsPointFeature"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#pointFeatureStandsFor">
    <rdfs:domain rdf:resource="#PointFeature"/>
    <rdfs:range rdf:resource="#OSMNode"/>
    <rdfs:subPropertyOf rdf:resource="#standsFor"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#shownBy">
    <owl:inverseOf rdf:resource="#shows"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#shows">
    <rdfs:domain rdf:resource="#SVGMap"/>
    <rdfs:range rdf:resource="#Feature"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#showsAreaFeature">
    <rdfs:range rdf:resource="#AreaFeature"/>
    <rdfs:subPropertyOf rdf:resource="#shows"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#showsLineFeature">
    <rdfs:range rdf:resource="#LineFeature"/>
    <rdfs:subPropertyOf rdf:resource="#shows"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#showsPointFeature">
    <rdfs:range rdf:resource="#PointFeature"/>
    <rdfs:subPropertyOf rdf:resource="#shows"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#standingFor">
    <owl:inverseOf rdf:resource="#standsFor"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#standingForAreaFeature">
    <rdfs:subPropertyOf rdf:resource="#standingFor"/>
    <owl:inverseOf rdf:resource="#areaFeatureStandsFor"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#standingForLineFeature">
    <rdfs:subPropertyOf rdf:resource="#standingFor"/>
    <owl:inverseOf rdf:resource="#lineFeatureStandsFor"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#standingForPointFeature">
    <rdfs:subPropertyOf rdf:resource="#standingFor"/>
    <owl:inverseOf rdf:resource="#pointFeatureStandsFor"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="#standsFor">
    <rdfs:domain rdf:resource="#Feature"/>
    <rdfs:range rdf:resource="#OSMData"/>
  </owl:ObjectProperty>

  <owl:ObjectProperty rdf:about="http://linkedgeodata.org/triplify/hasNode">
    <owl:equivalentProperty rdf:resource="#hasNode"/>
  </owl:ObjectProperty>
</rdf:RDF>

Patch for Osmarender

Here you can download a patch for Osmarender that's will make the renderes add a OWL description of the map to the SVG metadata tag.

Features

  • retrieve all features displayed within the map
  • for each feature get the labels labelling the feature and the SVG elements drawing the feature
  • for each feature get the OSM data from http://linkedgeodata.org which for which it stands

Todos

  • add semantic information about the SVG map itself (the bounding box, the used projection, the measure used, etc.)
  • add local SVG coordinates to the description of labels and features
  • extend OSMLabelOptimizer to handle the OWL description for easier parsing the map

Example

Here you find an example SVG file created with a patched version of Osmarender. For loading the OWL description in Swoop I created a xml file containing only the RDF part. To load the description, extract the xml file and load it into Swoop. After that load the ontology owl file from above and replace the defintions already loaded with the xml file. After that you can activate the Pellet reasoner and play around. Have fun:-)