PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl:  <http://www.w3.org/2002/07/owl#>
PREFIX dc:   <http://purl.org/dc/elements/1.1/>
PREFIX dct:  <http://purl.org/dc/terms/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

SELECT DISTINCT ?property ?title ?description ?domain ?range WHERE {
    # We define easy to find subClasses of rdfs:Class (ie owl:Class)
    ?propertyClass rdfs:subClassOf* rdf:Property  .

    {
        ?p rdfs:range ?propertyClass .
        ?thing ?p ?property .
    }
    UNION
    {
        ?p rdfs:domain ?propertyClass .
        ?property ?p ?thing .
    }
    UNION
    {
        # something that is a class is a class
        ?property rdf:type ?propertyClass .
    }

    # Get some useful metadata
    OPTIONAL {
        ?property ( rdfs:label | dc:title | dct:title | skos:prefLabel ) ?title .
    }
    OPTIONAL {
        ?property ( rdfs:comment | dc:description | dct:description | skos:definition ) ?description.
    }
    OPTIONAL {
        ?property ( rdfs:domain ) ?domain.
            FILTER(isIRI(?domain))
    }
    OPTIONAL {
        ?property ( rdfs:domain ) ?d.
        # Case 2: owl:unionOf list
        ?d owl:unionOf ?list .
        ?list rdf:rest*/rdf:first ?domain .
    }
    OPTIONAL {
        ?property ( rdfs:range ) ?range.
            FILTER(isIRI(?range))
    }
    OPTIONAL {
        ?property ( rdfs:range ) ?r.
        # Case 2: owl:unionOf list
        ?r owl:unionOf ?list .
        ?list rdf:rest*/rdf:first ?range .
    }
}
