Spatial Geometry#

About#

For spatial geometry Ocean InfoHub guidance will be to use the OGC GeoSPARQL vocabulary to express geometry using Well Known Text (WKT). The schema.org spatial types and properties are not well defined and difficult at times to reliably translate to geometries for use in more Open Geospatial Consortium (OGC) environments.

Note

schema.org expects a lat long (Y X) coordinate order, so be aware of that when you are defining your spatialCoverage, in the GeoShape polygon or box parameters.

Simple GeoSPARQL WKT#

The following is a simple example of how to embed a WKT string via GeoSPARQL into a JSON-LD record.
Well Know Text (WKT) is a OGC standard referenced at: https://www.ogc.org/standards/wkt-crs. A more accessible description and set of examples can be found at WikiPedia: Well-known text representation of geometry.

 1{
 2    "@context": {
 3      "@vocab": "https://schema.org/",
 4      "geosparql": "http://www.opengis.net/ont/geosparql#"
 5    },
 6    "@id": "https://example.org/permanentUrlToThisJsonDoc",
 7    "@type": "Dataset",
 8    "name": "Data set name",
 9    "geosparql:hasGeometry": {
10        "@type": "http://www.opengis.net/ont/sf#Point",
11        "geosparql:asWKT": {
12            "@type": "http://www.opengis.net/ont/geosparql#wktLiteral",
13            "@value": "POINT(-76 -18)"
14        },
15        "geosparql:crs": {
16            "@id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
17        }
18    }
19}

Line 4 declare the GeoSPARQL prefix for the vocabulary that we will leverage in this document.

Lines 9-17 are the GeoSPARQL node and property definitions. In this case our type is a simple point geometry. We then go on to declare the asWKT with a type and value. The value is our actual WKT string for our geometry. We can further declare the coordinate reference system (CRS) of the geometry using the crs property.

Hide code cell source
import json
from pyld import jsonld
import os, sys

currentdir = os.path.dirname(os.path.abspath(''))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)
from lib import jbutils

with open("../../../odis-in/dataGraphs/thematics/spatial/graphs/basic.json") as dgraph:
    doc = json.load(dgraph)

context = {
    "@vocab": "https://schema.org/",
}

compacted = jsonld.compact(doc, context)
jbutils.show_graph(compacted)
../../_images/1e38d88d2476aed0166ab0bdb9d45db398dbdfb6f4591c8b73e9375aef2a3f96.svg

WKT Bounding Box#

Note that WKT doesn’t directly have a bounding box, in that case you would need to use a polygon. The following is an example of a WKT string:

POLYGON((45.066730529474505 2.6430807905900235,47.395832091974505 2.6430807905900235,47.395832091974505 0.3588601145746598,45.066730529474505 0.3588601145746598,45.066730529474505 2.6430807905900235))

This following the pattern:

'POLYGON(x1 y1, x1 y2, x2 y2, x2 y1, x1 y1)'

Classic Schema.org#

Ocean InfoHub only recommends the use of Schema.org spatial geometries in the case where a provider wishes to be properly indexed by Google and to have the spatial information used by Google for maps. Note, the lack of spatial information will not prevent Google from indexing your resources.

Schema.org spatial geometries are not well defined in comparison to OGC standards and recommendations. Also, converting from Schema.org spatial to geometries in WKT or GeoJSON can be problematic. There are inconsistencies with Schema.org guidance for textual geometry representation and that of Well Known Text (WKT).

That said, if you desire to leverage Schema.org geometries an example follows. This is a simple example of the existing Schema.org pattern for a lat long value. There is the pending GeospatialGeometry which is a type Intangible (and not Place referenced by spatialCoverage). This will be a subtype of GeoShape.

Schema.org spatial relations are guided by DE-9IM.

 1{
 2  "@context": {
 3    "@vocab": "https://schema.org/"
 4  },
 5  "@id": "https://example.org/permanentUrlToThisJsonDoc",
 6  "@type": "Dataset",
 7  "name": "Data set name",
 8  "spatialCoverage": {
 9    "@type": "Place",
10    "geo": {
11      "@type": "GeoCoordinates",
12      "latitude": 39.3280,
13      "longitude": 120.1633
14    }
15  }
16}

Using a bounding box for your spatialCoverage is recommended, as it is easy to query & display downstream, such as:

 1    "spatialCoverage": {
 2        "@type": "Place",
 3        "geo": {
 4            "@type": "GeoShape",
 5            "description": "schema.org expects lat long (Y X) coordinate order.  Box syntax is: miny minx maxy maxx",
 6            "box": "-90 -180 90 -180"
 7        },
 8        "additionalProperty": {
 9            "@type": "PropertyValue",
10            "propertyID": "https://dbpedia.org/page/Spatial_reference_system",
11            "value": "https://www.w3.org/2003/01/geo/wgs84_pos"
12        }
13    },
Hide code cell source
import json
from pyld import jsonld
import os, sys

currentdir = os.path.dirname(os.path.abspath(''))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)
from lib import jbutils

with open("../../../odis-in/dataGraphs/thematics/spatial/graphs/sos.json") as dgraph:
    doc = json.load(dgraph)

context = {
    "@vocab": "https://schema.org/",
}

compacted = jsonld.compact(doc, context)
jbutils.show_graph(compacted)
../../_images/1c6cf336a91a95132ce8d6acac66081a372de23b17b4eea6c1a7006b265e824d.svg

Option review, SOS Issue 105#

There are several approaches to expressing spatial geometries in JSON-LD. While Ocean InfoHub will recommend the use of GeoSPARQL, it is worth noting that there are alternative and solid cases for using them

One such case could be the case where your WKT geometry string are highly detailed and as a result quite long. These might result in both very large JSON-LD documents that are hard to read and maintain. It may also be that this imparts a performance penalty in your GeoSPARQL queries.

It may be tha that you simplify your WKT geometry strings to a more basic form. Then link out to the detailed geometry in a separate document. The simplified WKT (or Schema.org spatial) make the documents smaller and easier to read and could help query performance. The resource can then point to a dereferencable URL for the detailed geometry.

ref Selfie: When linking out to complex geometries we recommend following: https://docs.ogc.org/per/20-067.html

From the referenced SOS issue 105:

 1{
 2    "@context": {
 3        "@version": 1.1,
 4        "geoblob": {
 5            "@id": "http://example.com/vocab/json",
 6            "@type": "@json"
 7        },
 8        "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
 9        "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
10        "xsd": "http://www.w3.org/2001/XMLSchema#",
11        "description": "http://igsn.org/core/v1/description",
12        "geosparql": "http://www.opengis.net/ont/geosparql#",
13        "schema": "https://schema.org/"
14    },
15    "@id": "https://samples.earth/id/do/bqs2dn2u6s73o70jdup0",
16    "@type": "http://igsn.org/core/v1/Sample",
17    "description": "A fake ID for testing",
18    "schema:subjectOf": [
19        {
20            "schema:url": "https://samples.earth/id/do/bqs2dn2u6s73o70jdup0.geojson",
21            "@type": "schema:DigitalDocument",
22            "schema:format": [
23                "application/vnd.geo+json"
24            ],
25            "schema:conformsTo": "https://igsn.org/schema/spatial.schema.json"
26        }
27    ],
28    "geosparql:hasGeometry": {
29        "@id": "_:N98e75cacc29f40deb555eb583cb162dc",
30        "@type": "http://www.opengis.net/ont/sf#Point",
31        "geosparql:asWKT": {
32            "@type": "http://www.opengis.net/ont/geosparql#wktLiteral",
33            "@value": "POINT(-76 -18)"
34        },
35        "geosparql:crs": {
36            "@id": "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
37        }
38    },
39    "geoblob": {
40        "type": "GeometryCollection",
41        "geometries": [{
42            "type": "Point",
43            "coordinates": [-76, -18]
44        }]
45    },
46    "schema:spatialCoverage": {
47        "@type": "schema:Place",
48        "schema:geo": {
49          "@type": "schema:GeoCoordinates",
50          "schema:latitude": -18,
51          "schema:longitude": -76
52        }
53      }
54}
Hide code cell source
import json
from pyld import jsonld
import os, sys

currentdir = os.path.dirname(os.path.abspath(''))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)
from lib import jbutils

with open("../../../odis-in/dataGraphs/thematics/spatial/graphs/issue105.json") as dgraph:
    doc = json.load(dgraph)

context = {
    "@vocab": "https://schema.org/",
}

compacted = jsonld.compact(doc, context)
jbutils.show_graph(compacted)
../../_images/070b8bc8308ae3e7b764417eda6f85c867b7ea36c3fdcffd9c8c236a77c26ab8.svg

References#