3lbits / CIM4NoUtility

CIM for the Norwegian Power Utility
Creative Commons Attribution Share Alike 4.0 International
20 stars 7 forks source link

use geo:asGeoJSON instead of (or in addition to) GeoJSON-LD #337

Open VladimirAlexiev opened 5 months ago

VladimirAlexiev commented 5 months ago

https://github.com/3lbits/Grunnprofil/raw/main/DIGIN10/Grid/CIMJSON-LD/DIGIN10-30-WattApp-GL.geojson is a GeoJSON file with a JSON-LD context.

So we can convert it to turtle to see it more clearly (or trig, but it contains no named graphs, so the result is the same):

curl -s https://raw.githubusercontent.com/3lbits/Grunnprofil/main/DIGIN10/Grid/CIMJSON-LD/DIGIN10-30-WattApp-GL.geojson | riot --syntax jsonld --formatted trig -

Here's the result (shortened):

<urn:uuid:4e51e598-8948-4c85-b151-f44ffddc5545>
        rdf:type                    cim:Feeder ;
        cim:IdentifiedObject.mRID   "urn:uuid:4e51e598-8948-4c85-b151-f44ffddc5545" ;
        cim:IdentifiedObject.name   "Sandefjord_næring_og_industri_område"@no , "Sandefjord_business_and_industry_area"@en .

<urn:uuid:971c4254-5365-4aaf-8fa6-02658b3f8e05>
        rdf:type              dcat:Dataset , geojson:FeatureCollection ;
        geojson:features      <urn:uuid:58aee5a8-0cd9-467e-b5e7-180ac161d9b8> .

<urn:uuid:58aee5a8-0cd9-467e-b5e7-180ac161d9b8>
        rdf:type            geojson:Feature ;
        geojson:geometry    [ rdf:type             geojson:Polygon ;
                              geojson:coordinates  ( ( ( 1.022902098465971E1 5.924698254345179E1 ) ( 1.018420614097019E1 5.919982925336586E1 ) ( 1.014288336302374E1 5.913888033657065E1 ) ( 1.020244248428966E1 5.913758660552921E1 ) ( 1.027635787582801E1 5.916126409167293E1 ) ( 1.031050251863826E1 5.922922084872761E1 ) ( 1.022902098465971E1 5.924698254345179E1 ) ) )
                            ] ;
        geojson:properties  <urn:uuid:4e51e598-8948-4c85-b151-f44ffddc5545> .

There are some problems here:

  1. GeoJSON-LD is not any sort of standard. It's a 1-person proposal https://geojson.org/geojson-ld/
  2. Semantic repositories do not index geometries represented in this way. Instead, they support GeoSPARQL
  3. The business object cim:Feeder is considered secondary to the Feature, a mere set of properties attached to the Feature. But in reality it's a distinct RDF resource with its own identity (URL).
  4. Most CIM resources have no geometry, so this "inverted" pattern does not apply to them. So all resources need to reside in the named graph representing the model (Dataset), and such named graph is missing above.

The above is a nice representation for geo toolkits that don't support semantics (eg the Github preview of https://github.com/3lbits/Grunnprofil/blob/main/DIGIN10/Grid/CIMJSON-LD/DIGIN10-30-WattApp-GL.geojson is nice)

But for semantic tools (eg semantic repositories), I propose to use GeoSPARQL. 1.0 supports WKT and GML ontologies. WKT is simpler and widely used, and could look something like this:

<urn:uuid:971c4254-5365-4aaf-8fa6-02658b3f8e05>
        rdf:type              dcat:Dataset.

<urn:uuid:971c4254-5365-4aaf-8fa6-02658b3f8e05> {

<urn:uuid:4e51e598-8948-4c85-b151-f44ffddc5545>
        rdf:type                    cim:Feeder, geo:Feature ;
        cim:IdentifiedObject.mRID   "urn:uuid:4e51e598-8948-4c85-b151-f44ffddc5545" ;
        cim:IdentifiedObject.name   "Sandefjord_næring_og_industri_område"@no , "Sandefjord_business_and_industry_area"@en ;
        geo:hasGeometry <urn:uuid:58aee5a8-0cd9-467e-b5e7-180ac161d9b8>.

<urn:uuid:58aee5a8-0cd9-467e-b5e7-180ac161d9b8>
        rdf:type            geo:Geometry ;
        geo:asWKT """Polygon (( 1.022902098465971E1 5.924698254345179E1, 1.018420614097019E1 5.919982925336586E1, 1.014288336302374E1 5.913888033657065E1, 1.020244248428966E1 5.913758660552921E1, 1.027635787582801E1 5.916126409167293E1, 1.031050251863826E1 5.922922084872761E1, 1.022902098465971E1 5.924698254345179E1))"""^^geo:wktLiteral.
}

GML is an XML format and is also widely used, eg it's the basis of CityGML.

GeoSPARQL 1.1 supports KML and GeoJSON geometries. To use the latter, you'd change only the last line:

  geo:asGeoJSON """{type: "Polygon",
coordinates: [[[10.229020984659712,59.24698254345179], [10.18420614097019,59.19982925336586], [10.142883363023742,59.13888033657065], [10.202442484289662,59.13758660552921], [10.276357875828012,59.16126409167293], [10.310502518638259,59.22922084872761], [10.229020984659712,59.24698254345179]]]}"""^^geo:geoJSONLiteral.

Important benefits of GeoSPARQL include:

A particular use case in electricity could be to compute easement, e.g. the area around a transmission line that is prohibited for building use. The function geof:buffer(geom,distance) can be used for that.

You can use JSON-LD context to capture part of a JSON payload as an RDF literal. See https://github.com/w3c/json-ld-syntax/issues/425 for details.

tviegut commented 5 months ago

@VladimirAlexiev , thanks for your work/assistance in this area. This will offer a great opportunity for discussion on Friday I would assume.

VladimirAlexiev commented 3 months ago

@Sveino As per discussion in https://github.com/w3c/json-ld-syntax/issues/425, I made a typo: the property is geo:asGeoJSON not geo:asGeoJSONLiteral. I fixed that in the example above.

ThomasRanvikEriksen commented 3 months ago

Hello, do you have any suggestion for how this will be in a jsonld? I have a couple of variants here:

Variant1

{
    "@context": {
        "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
        "cim": "https://cim.ucaiug.io/ns#",
        "eu": "http://iec.ch/TC57/CIM100-European#",
        "dcterms": "http://purl.org/dc/terms/",
        "dcat": "http://www.w3.org/ns/dcat#",
        "prov": "http://www.w3.org/ns/prov#",
        "xsd": "http://www.w3.org/2001/XMLSchema#",
        "nc-no": "https://cim4.eu/ns/nc-no#",
        "geo": "http://www.opengis.net/ont/geosparql#"
    },
    "@graph": [
        {
            "@id": "urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "@type": ["nc-no:ACLineSegmentSpan", "geo:Feature"],
            "cim:IdentifiedObject.mRID": "802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "cim:IdentifiedObject.description": "ACLineSegmentSpan 2",
            "cim:IdentifiedObject.name": "ACLSS_2",
            "nc-no:PowerSystemResource.locationMethod": {
                "@id": "nc-no:locationMethodKind.measured"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleMarkingKind": {
                "@id": "nc-no:LineMarkingKind.colourMarking"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleLightingKind": {
                "@id": "nc-no:LineLightingKind.lit"
            },
            "nc-no:ACLineSegmentSpan.maxWidth": 3.97,
            "nc-no:ACLineSegmentSpan.maxHeight": 153.23,
            "nc-no:ACLineSegmentSpan.spanWireLength": 63.01,
            "nc-no:ACLineSegmentSpan.ACLineSegment": [
                {
                    "@id": "urn:uuid:f8b08d2f-9110-4016-8aca-bdcec9fa02be"
                }
            ],
            "geo:hasGeometry": {
                "geo:asWKT": "\"LINESTRING (6502691.542169236 972304.9284766684,6502691.540990914 972304.9204293368)\"^^geo:wktLiteral ;",
                "geo:asGeoJSON": "\"{\"type\": \"LineString\", \"coordinates\": [[[8.7007293714585,58.41467201058505], [8.7007293,58.414672]]]}\"^^geo:geoJSONLiteral ;"
            }
        }
    ]
}

Variant2

{
    "@context": {
        "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
        "cim": "https://cim.ucaiug.io/ns#",
        "eu": "http://iec.ch/TC57/CIM100-European#",
        "dcterms": "http://purl.org/dc/terms/",
        "dcat": "http://www.w3.org/ns/dcat#",
        "prov": "http://www.w3.org/ns/prov#",
        "xsd": "http://www.w3.org/2001/XMLSchema#",
        "nc-no": "https://cim4.eu/ns/nc-no#",
        "geo": "http://www.opengis.net/ont/geosparql#"
    },
    "@graph": [
        {
            "@id": "urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "@type": "nc-no:ACLineSegmentSpan, geo:Feature",
            "cim:IdentifiedObject.mRID": "802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "cim:IdentifiedObject.description": "ACLineSegmentSpan 2",
            "cim:IdentifiedObject.name": "ACLSS_2",
            "nc-no:PowerSystemResource.locationMethod": {
                "@id": "nc-no:locationMethodKind.measured"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleMarkingKind": {
                "@id": "nc-no:LineMarkingKind.colourMarking"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleLightingKind": {
                "@id": "nc-no:LineLightingKind.lit"
            },
            "nc-no:ACLineSegmentSpan.maxWidth": 3.97,
            "nc-no:ACLineSegmentSpan.maxHeight": 153.23,
            "nc-no:ACLineSegmentSpan.spanWireLength": 63.01,
            "nc-no:ACLineSegmentSpan.ACLineSegment": [
                {
                    "@id": "urn:uuid:f8b08d2f-9110-4016-8aca-bdcec9fa02be"
                }
            ],
            "geo:hasGeometry": {
                "geo:asWKT": "\"LINESTRING (6502691.542169236 972304.9284766684,6502691.540990914 972304.9204293368)\"^^geo:wktLiteral ;",
                "geo:asGeoJSON": "\"{\"type\": \"LineString\", \"coordinates\": [[[8.7007293714585,58.41467201058505], [8.7007293,58.414672]]]}\"^^geo:geoJSONLiteral ;"
            }
        }
    ]
}

Variant3

{
    "@context": {
        "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
        "cim": "https://cim.ucaiug.io/ns#",
        "eu": "http://iec.ch/TC57/CIM100-European#",
        "dcterms": "http://purl.org/dc/terms/",
        "dcat": "http://www.w3.org/ns/dcat#",
        "prov": "http://www.w3.org/ns/prov#",
        "xsd": "http://www.w3.org/2001/XMLSchema#",
        "nc-no": "https://cim4.eu/ns/nc-no#",
        "geo": "http://www.opengis.net/ont/geosparql#"
    },
    "@graph": [
        {
            "@id": "urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "@type": "nc-no:ACLineSegmentSpan",
            "cim:IdentifiedObject.mRID": "802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "cim:IdentifiedObject.description": "ACLineSegmentSpan 2",
            "cim:IdentifiedObject.name": "ACLSS_2",
            "nc-no:PowerSystemResource.locationMethod": {
                "@id": "nc-no:locationMethodKind.measured"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleMarkingKind": {
                "@id": "nc-no:LineMarkingKind.colourMarking"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleLightingKind": {
                "@id": "nc-no:LineLightingKind.lit"
            },
            "nc-no:ACLineSegmentSpan.maxWidth": 3.97,
            "nc-no:ACLineSegmentSpan.maxHeight": 153.23,
            "nc-no:ACLineSegmentSpan.spanWireLength": 63.01,
            "nc-no:ACLineSegmentSpan.ACLineSegment": [
                {
                    "@id": "urn:uuid:f8b08d2f-9110-4016-8aca-bdcec9fa02be"
                }
            ],
            "nc-no:PowerSystemResource.SpatialObject": {
                "@id": "urn:uuid:2b25854f-be7c-4511-9251-9b085207c15c",
                "@type": "geo:Feature",
                "geo:hasGeometry": {
                    "geo:asWKT": "\"LINESTRING (6502691.542169236 972304.9284766684,6502691.540990914 972304.9204293368)\"^^geo:wktLiteral ;",
                    "geo:asGeoJSON": "\"{\"type\": \"LineString\", \"coordinates\": [[[8.7007293714585,58.41467201058505], [8.7007293,58.414672]]]}\"^^geo:geoJSONLiteral ;"
                }
            }
        }
    ]
}

Variant4

{
    "@context": {
        "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
        "cim": "https://cim.ucaiug.io/ns#",
        "eu": "http://iec.ch/TC57/CIM100-European#",
        "dcterms": "http://purl.org/dc/terms/",
        "dcat": "http://www.w3.org/ns/dcat#",
        "prov": "http://www.w3.org/ns/prov#",
        "xsd": "http://www.w3.org/2001/XMLSchema#",
        "nc-no": "https://cim4.eu/ns/nc-no#",
        "geo": "http://www.opengis.net/ont/geosparql#"
    },
    "@graph": [
        {
            "@id": "urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "@type": "nc-no:ACLineSegmentSpan",
            "cim:IdentifiedObject.mRID": "802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
            "cim:IdentifiedObject.description": "ACLineSegmentSpan 2",
            "cim:IdentifiedObject.name": "ACLSS_2",
            "nc-no:PowerSystemResource.locationMethod": {
                "@id": "nc-no:locationMethodKind.measured"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleMarkingKind": {
                "@id": "nc-no:LineMarkingKind.colourMarking"
            },
            "nc-no:ACLineSegmentSpan.aviationObstacleLightingKind": {
                "@id": "nc-no:LineLightingKind.lit"
            },
            "nc-no:ACLineSegmentSpan.maxWidth": 3.97,
            "nc-no:ACLineSegmentSpan.maxHeight": 153.23,
            "nc-no:ACLineSegmentSpan.spanWireLength": 63.01,
            "nc-no:ACLineSegmentSpan.ACLineSegment": [
                {
                    "@id": "urn:uuid:f8b08d2f-9110-4016-8aca-bdcec9fa02be"
                }
            ],
            "nc-no:PowerSystemResource.SpatialObject": {
                "geo:asWKT": "\"LINESTRING (6502691.542169236 972304.9284766684,6502691.540990914 972304.9204293368)\"^^geo:wktLiteral ;",
                "geo:asGeoJSON": "\"{\"type\": \"LineString\", \"coordinates\": [[[8.7007293714585,58.41467201058505], [8.7007293,58.414672]]]}\"^^geo:geoJSONLiteral ;"
            }
        }
    ]
}
ThomasRanvikEriksen commented 3 months ago

After som more research by @EmilieSkog we now see that jsonld supports @type as a list. So we believe variant1 is probably the best solution we have so far.

VladimirAlexiev commented 3 months ago

Variant5:

{
  "@context": {
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "cim": "https://cim.ucaiug.io/ns#",
    "eu": "http://iec.ch/TC57/CIM100-European#",
    "dcterms": "http://purl.org/dc/terms/",
    "dcat": "http://www.w3.org/ns/dcat#",
    "prov": "http://www.w3.org/ns/prov#",
    "xsd": "http://www.w3.org/2001/XMLSchema#",
    "nc-no": "https://cim4.eu/ns/nc-no#",
    "geo": "http://www.opengis.net/ont/geosparql#"
  },
  "@graph": [
    {
      "@id": "urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
      "@type": ["nc-no:ACLineSegmentSpan", "geo:Feature"],
      "cim:IdentifiedObject.mRID": "802a2294-3cc5-4d3a-b2ba-57706abfe8ed",
      "cim:IdentifiedObject.description": "ACLineSegmentSpan 2",
      "cim:IdentifiedObject.name": "ACLSS_2",
      "nc-no:PowerSystemResource.locationMethod": {
        "@id": "nc-no:locationMethodKind.measured"
      },
      "nc-no:ACLineSegmentSpan.aviationObstacleMarkingKind": {
        "@id": "nc-no:LineMarkingKind.colourMarking"
      },
      "nc-no:ACLineSegmentSpan.aviationObstacleLightingKind": {
        "@id": "nc-no:LineLightingKind.lit"
      },
      "nc-no:ACLineSegmentSpan.maxWidth": 3.97,
      "nc-no:ACLineSegmentSpan.maxHeight": 153.23,
      "nc-no:ACLineSegmentSpan.spanWireLength": 63.01,
      "nc-no:ACLineSegmentSpan.ACLineSegment": [
        {
          "@id": "urn:uuid:f8b08d2f-9110-4016-8aca-bdcec9fa02be"
        }
      ],
      "geo:hasGeometry": {
        "@id": "urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed_geometry",
        "@type": "geo:Geometry",
        "geo:asWKT": {"@value":"LINESTRING (6502691.542169236 972304.9284766684,6502691.540990914 972304.9204293368)", "@type":"geo:wktLiteral"},
        "geo:asGeoJSON": {"@value":"{\"type\": \"LineString\", \"coordinates\": [[[8.7007293714585,58.41467201058505], [8.7007293,58.414672]]]}", "@type":"geo:geoJSONLiteral"}
      }
    }
  ]
}

You should always check by converting your JSONLD to another representation (Turtle) to ensure you got what you expected. In this case you can do it with Jena RIOT:

# riot -out ttl variant5.jsonld
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix cim: <https://cim.ucaiug.io/ns#> .
@prefix eu: <http://iec.ch/TC57/CIM100-European#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix nc-no: <https://cim4.eu/ns/nc-no#> .
@prefix geo: <http://www.opengis.net/ont/geosparql#> .

<urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed>
        rdf:type                   nc-no:ACLineSegmentSpan ;
        rdf:type                   geo:Feature ;
        geo:hasGeometry            <urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed_geometry> ;
        cim:IdentifiedObject.description  "ACLineSegmentSpan 2" ;
        cim:IdentifiedObject.mRID  "802a2294-3cc5-4d3a-b2ba-57706abfe8ed" ;
        cim:IdentifiedObject.name  "ACLSS_2" ;
        nc-no:ACLineSegmentSpan.ACLineSegment  <urn:uuid:f8b08d2f-9110-4016-8aca-bdcec9fa02be> ;
        nc-no:ACLineSegmentSpan.aviationObstacleLightingKind  nc-no:LineLightingKind.lit ;
        nc-no:ACLineSegmentSpan.aviationObstacleMarkingKind  nc-no:LineMarkingKind.colourMarking ;
        nc-no:ACLineSegmentSpan.maxHeight  1.5323E2 ;
        nc-no:ACLineSegmentSpan.maxWidth  3.97E0 ;
        nc-no:ACLineSegmentSpan.spanWireLength  6.301E1 ;
        nc-no:PowerSystemResource.locationMethod  nc-no:locationMethodKind.measured .

<urn:uuid:802a2294-3cc5-4d3a-b2ba-57706abfe8ed_geometry>
        rdf:type       geo:Geometry ;
        geo:asGeoJSON  "{\"type\": \"LineString\", \"coordinates\": [[[8.7007293714585,58.41467201058505], [8.7007293,58.414672]]]}"^^geo:geoJSONLiteral ;
        geo:asWKT      "LINESTRING (6502691.542169236 972304.9284766684,6502691.540990914 972304.9204293368)"^^geo:wktLiteral .

IMPORTANT:

@ThomasRanvikEriksen can you point me to some NC resources/specs?

VladimirAlexiev commented 3 months ago

Weekly call with @Sveino