BASE <https://w3id.org/itsdata/location/v1/>
PREFIX : <https://w3id.org/itsdata/location/v1/>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX sh: <http://www.w3.org/ns/shacl#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX its-core: <https://w3id.org/itsdata/core/v1/>
PREFIX its-sh: <https://w3id.org/itsdata/shacl/v1/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

:ItinerarySHACL
    a owl:Ontology ;
    dcterms:title "SHACL rules for ITS Itinerary Referencing"@en ;
    skos:definition "SHACL shapes for itineraries and itinerary representations."@en ;
    its-core:draft "true" ;
    dcterms:created "Draft" ;
    dcterms:modified "2026-05-01"^^xsd:date ;
    owl:versionInfo "0.0.1" ;
    owl:versionIRI <ItinerarySHACL/r0.1> ;
    owl:imports :LocationCoreSHACL .

#################################################################
# Node shapes
#################################################################

:ItineraryShape
    a sh:NodeShape ;
    sh:targetClass :Itinerary ;
    sh:and ( :FeatureShape ) ;
    sh:property [
        sh:path :hasPrimaryRepresentation ;
        sh:node its-sh:ExactlyOneShape ;
        sh:class :ItineraryRepresentation ;
        sh:message "An Itinerary must have exactly one :hasPrimaryRepresentation value." ;
    ] ;
    sh:property [
        sh:path :hasAlternativeRepresentation ;
        sh:node its-sh:UnlimitedShape ;
        sh:class :ItineraryRepresentation ;
    ] .

:ItineraryRepresentationShape
    a sh:NodeShape ;
    sh:targetClass :ItineraryRepresentation ;
    sh:and ( :GeometryShape ) .

:ItineraryByWaypointsShape
    a sh:NodeShape ;
    sh:targetClass :ItineraryByWaypoints ;
    sh:and ( :ItineraryRepresentationShape ) ;
    sh:property [
        sh:path :hasWaypointList ;
        sh:node its-sh:ExactlyOneShape ;
        sh:class :LocationList ;
        sh:message "ItineraryByWaypoints must have exactly one LocationList via :hasWaypointList." ;
    ] ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "ItineraryByWaypoints must contain at least two member Locations." ;
        sh:select """
            PREFIX : <https://w3id.org/itsdata/location/v1/>
            PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
            SELECT $this ?list
            WHERE {
              $this :hasWaypointList ?list .
              FILTER NOT EXISTS {
                SELECT ?list WHERE {
                  ?list rdf:rest*/rdf:first ?loc .
                }
                GROUP BY ?list
                HAVING (COUNT(?loc) >= 2)
              }
            }
        """ ;
    ] .

:LocationListShape
    a sh:NodeShape ;
    sh:targetClass :LocationList ;
    sh:property [
        sh:path rdf:first ;
        sh:class :Location ;
        sh:node its-sh:ExactlyOneShape ;
    ] ;
    sh:property [
        sh:path rdf:rest ;
        sh:or ( [ sh:hasValue rdf:nil ] [ sh:class :LocationList ] ) ;
        sh:node its-sh:ExactlyOneShape ;
    ] ;
    sh:closed true ;
    sh:ignoredProperties ( rdf:type ) ;
    sh:message "A LocationList must be a well-formed rdf:List of Locations." .

