| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| |
| |
| <title>Apache Jena - Apache Jena GeoSPARQL</title> |
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| |
| <link href="/css/bootstrap.min.css" rel="stylesheet" media="screen"> |
| <link href="/css/bootstrap-icons.css" rel="stylesheet" media="screen"><link rel="stylesheet" type="text/css" href="https://jena.apache.org/sass/jena.1b17c39a117e22b46db4c66f6395dc27c134a60377d87d2d5745b8600eb69722.css" integrity="sha256-GxfDmhF+IrRttMZvY5XcJ8E0pgN32H0tV0W4YA62lyI="> |
| <link rel="shortcut icon" href="/images/favicon.ico" /> |
| |
| </head> |
| |
| <body> |
| |
| <nav class="navbar navbar-expand-lg bg-body-tertiary" role="navigation"> |
| <div class="container"> |
| <div class="navbar-header"> |
| <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> |
| <span class="navbar-toggler-icon"></span> |
| </button> |
| <a class="navbar-brand" href="/index.html"> |
| <img class="logo-menu" src="/images/jena-logo/jena-logo-notext-small.png" alt="jena logo">Apache Jena</a> |
| </div> |
| |
| <div class="collapse navbar-collapse" id="navbarNav"> |
| <ul class="navbar-nav me-auto mb-2 mb-lg-0"> |
| <li id="homepage" class="nav-item"><a class="nav-link" href="/index.html"><span class="bi-house"></span> Home</a></li> |
| <li id="download" class="nav-item"><a class="nav-link" href="/download/index.cgi"><span class="bi-download"></span> Download</a></li> |
| <li class="nav-item dropdown"> |
| <a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><span class="bi-journal"></span> Learn <b class="caret"></b></a> |
| <ul class="dropdown-menu"> |
| <li class="dropdown-header">Tutorials</li> |
| <li><a class="dropdown-item" href="/tutorials/index.html">Overview</a></li> |
| <li><a class="dropdown-item" href="/documentation/fuseki2/index.html">Fuseki Triplestore</a></li> |
| <li><a class="dropdown-item" href="/documentation/notes/index.html">How-To's</a></li> |
| <li><a class="dropdown-item" href="/documentation/query/manipulating_sparql_using_arq.html">Manipulating SPARQL using ARQ</a></li> |
| <li><a class="dropdown-item" href="/tutorials/rdf_api.html">RDF core API tutorial</a></li> |
| <li><a class="dropdown-item" href="/tutorials/sparql.html">SPARQL tutorial</a></li> |
| <li><a class="dropdown-item" href="/tutorials/using_jena_with_eclipse.html">Using Jena with Eclipse</a></li> |
| <li class="dropdown-divider"></li> |
| <li class="dropdown-header">References</li> |
| <li><a class="dropdown-item" href="/documentation/index.html">Overview</a></li> |
| <li><a class="dropdown-item" href="/documentation/query/index.html">ARQ (SPARQL)</a></li> |
| <li><a class="dropdown-item" href="/documentation/io/">RDF I/O</a></li> |
| <li><a class="dropdown-item" href="/documentation/assembler/index.html">Assembler</a></li> |
| <li><a class="dropdown-item" href="/documentation/tools/index.html">Command-line tools</a></li> |
| <li><a class="dropdown-item" href="/documentation/rdfs/">Data with RDFS Inferencing</a></li> |
| <li><a class="dropdown-item" href="/documentation/geosparql/index.html">GeoSPARQL</a></li> |
| <li><a class="dropdown-item" href="/documentation/inference/index.html">Inference API</a></li> |
| <li><a class="dropdown-item" href="/documentation/ontology/">Ontology API</a></li> |
| <li><a class="dropdown-item" href="/documentation/permissions/index.html">Permissions</a></li> |
| <li><a class="dropdown-item" href="/documentation/extras/querybuilder/index.html">Query Builder</a></li> |
| <li><a class="dropdown-item" href="/documentation/rdf/index.html">RDF API</a></li> |
| <li><a class="dropdown-item" href="/documentation/rdfconnection/">RDF Connection - SPARQL API</a></li> |
| <li><a class="dropdown-item" href="/documentation/rdfstar/index.html">RDF-star</a></li> |
| <li><a class="dropdown-item" href="/documentation/shacl/index.html">SHACL</a></li> |
| <li><a class="dropdown-item" href="/documentation/shex/index.html">ShEx</a></li> |
| <li><a class="dropdown-item" href="/documentation/tdb/index.html">TDB</a></li> |
| <li><a class="dropdown-item" href="/documentation/tdb2/index.html">TDB2</a></li> |
| <li><a class="dropdown-item" href="/documentation/query/text-query.html">Text Search</a></li> |
| </ul> |
| </li> |
| |
| <li class="nav-item dropdown"> |
| <a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><span class="bi-journal-code"></span> Javadoc <b class="caret"></b></a> |
| <ul class="dropdown-menu"> |
| <li><a class="dropdown-item" href="/documentation/javadoc.html">All Javadoc</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/arq/">ARQ</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/fuseki2/">Fuseki</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/geosparql/">GeoSPARQL</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/jena/">Jena Core</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/permissions/">Permissions</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/extras/querybuilder/">Query Builder</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/shacl/">SHACL</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/tdb/">TDB</a></li> |
| <li><a class="dropdown-item" href="/documentation/javadoc/text/">Text Search</a></li> |
| </ul> |
| </li> |
| </ul> |
| <form class="d-flex" role="search" action="/search" method="GET"> |
| <div class="input-group"> |
| <input class="form-control border-end-0 border m-0" type="search" name="q" id="search-query" placeholder="Search...." aria-label="Search" style="width: 10rem;"> |
| <button class="btn btn-outline-secondary border-start-0 border" type="submit"> |
| <i class="bi-search"></i> |
| </button> |
| </div> |
| </form> |
| <ul class="navbar-nav"> |
| <li id="ask" class="nav-item"><a class="nav-link" href="/help_and_support/index.html" title="Ask"><span class="bi-patch-question"></span><span class="text-body d-none d-xxl-inline"> Ask</span></a></li> |
| |
| <li class="nav-item dropdown"> |
| <a href="#" title="Get involved" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><span class="bi-megaphone"></span><span class="text-body d-none d-xxl-inline"> Get involved </span><b class="caret"></b></a> |
| <ul class="dropdown-menu"> |
| <li><a class="dropdown-item" href="/getting_involved/index.html">Contribute</a></li> |
| <li><a class="dropdown-item" href="/help_and_support/bugs_and_suggestions.html">Report a bug</a></li> |
| <li class="dropdown-divider"></li> |
| <li class="dropdown-header">Project</li> |
| <li><a class="dropdown-item" href="/about_jena/about.html">About Jena</a></li> |
| <li><a class="dropdown-item" href="/about_jena/architecture.html">Architecture</a></li> |
| <li><a class="dropdown-item" href="/about_jena/citing.html">Citing</a></li> |
| <li><a class="dropdown-item" href="/about_jena/team.html">Project team</a></li> |
| <li><a class="dropdown-item" href="/about_jena/contributions.html">Related projects</a></li> |
| <li><a class="dropdown-item" href="/about_jena/roadmap.html">Roadmap</a></li> |
| <li><a class="dropdown-item" href="/about_jena/security-advisories.html">Security Advisories</a></li> |
| <li class="dropdown-divider"></li> |
| <li class="dropdown-header">ASF</li> |
| <li><a class="dropdown-item" href="https://www.apache.org/">Apache Software Foundation</a></li> |
| <li><a class="dropdown-item" href="https://www.apache.org/foundation/sponsorship.html">Become a Sponsor</a></li> |
| <li><a class="dropdown-item" href="https://www.apache.org/licenses/LICENSE-2.0">License</a></li> |
| <li><a class="dropdown-item" href="https://www.apache.org/security/">Security</a></li> |
| <li><a class="dropdown-item" href="https://www.apache.org/foundation/thanks.html">Thanks</a></li> |
| </ul> |
| </li> |
| |
| |
| |
| |
| <li class="nav-item" id="edit"><a class="nav-link" href="https://github.com/apache/jena-site/edit/main/source/documentation/geosparql/__index.md" title="Edit this page on GitHub"><span class="bi-pencil-square"></span><span class="text-body d-none d-xxl-inline"> Edit this page</span></a></li> |
| </ul> |
| </div> |
| </div> |
| </nav> |
| |
| <div class="container"> |
| <div class="row"> |
| <div class="col-md-12"> |
| |
| <div id="breadcrumbs"> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| <ol class="breadcrumb mt-4 p-2 bg-body-tertiary">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| <li class="breadcrumb-item"><a href='/documentation'>DOCUMENTATION</a></li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| <li class="breadcrumb-item active">GEOSPARQL</li>
|
|
|
|
|
|
|
|
|
| </ol>
|
|
|
|
|
|
|
| |
| </div> |
| <h1 class="title">Apache Jena GeoSPARQL</h1> |
| |
| |
| <main class="d-flex flex-xl-row flex-column"> |
| |
| <aside class="text-muted align-self-start mb-3 p-0 d-xl-none d-block"> |
| <h2 class="h6 sticky-top m-0 p-2 bg-body-tertiary">On this page</h2> |
| <nav id="TableOfContents"> |
| <ul> |
| <li><a href="#getting-started">Getting Started</a></li> |
| <li><a href="#features">Features</a></li> |
| <li><a href="#additional-features">Additional Features</a> |
| <ul> |
| <li><a href="#sparql-query-configuration">SPARQL Query Configuration</a></li> |
| <li><a href="#querying-datasets--models-with-sparql">Querying Datasets & Models with SPARQL</a></li> |
| <li><a href="#api-the-library-can-be-used-as-an-api-in-java--the-main-class-to-handle">API The library can be used as an API in Java. The main class to handle</a></li> |
| </ul> |
| </li> |
| <li><a href="#key-dependencies">Key Dependencies</a> |
| <ul> |
| <li><a href="#geosparql">GeoSPARQL</a></li> |
| <li><a href="#apache-sissis_data-environment-variable">Apache SIS/SIS_DATA Environment Variable</a></li> |
| <li><a href="#java-topology-suite">Java Topology Suite</a></li> |
| </ul> |
| </li> |
| <li><a href="#note">Note</a> |
| <ul> |
| <li><a href="#geosparql-schema">GeoSPARQL Schema</a></li> |
| <li><a href="#spatial-relations">Spatial Relations</a></li> |
| <li><a href="#spatial-relations-and-geometry-shapestypes">Spatial Relations and Geometry Shapes/Types</a></li> |
| <li><a href="#equals-relations">Equals Relations</a></li> |
| <li><a href="#query-rewrite-extension">Query Rewrite Extension</a></li> |
| <li><a href="#dataset-conversion">Dataset Conversion</a></li> |
| <li><a href="#spatial-index">Spatial Index</a></li> |
| <li><a href="#units-uri">Units URI</a></li> |
| </ul> |
| </li> |
| <li><a href="#geography-markup-language-support-gml">Geography Markup Language Support (GML)</a></li> |
| <li><a href="#apache-jena-spatial-functionswgs84-geo-predicates">Apache Jena Spatial Functions/WGS84 Geo Predicates</a> |
| <ul> |
| <li><a href="#supported-features">Supported Features</a></li> |
| <li><a href="#filter-functions">Filter Functions</a></li> |
| <li><a href="#property-functions">Property Functions</a></li> |
| </ul> |
| </li> |
| <li><a href="#geometry-property-filter-functions">Geometry Property Filter Functions</a></li> |
| <li><a href="#future-work">Future Work</a></li> |
| <li><a href="#contributors">Contributors</a></li> |
| <li><a href="#why-use-this-implementation">Why Use This Implementation?</a></li> |
| </ul> |
| </nav> |
| </aside> |
| <article class="flex-column me-lg-4"> |
| <p>An implementation of GeoSPARQL 1.0 standard for SPARQL query or API.</p> |
| <p>Integration with Fuseki is provided either by using the |
| <a href="geosparql-assembler.html">GeoSPARQL assembler</a> or using the self-contained original |
| <a href="geosparql-fuseki.html">jena-fuseki-geosparql</a>. In either case, this page |
| describes the GeoSPARQL supported features.</p> |
| <h2 id="getting-started">Getting Started</h2> |
| <p>GeoSPARQL Jena can be accessed as a library using Maven etc. from Maven Central.</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#008000;font-weight:bold"><dependency></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#008000;font-weight:bold"><groupId></span>org.apache.jena<span style="color:#008000;font-weight:bold"></groupId></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#008000;font-weight:bold"><artifactId></span>jena-geosparql<span style="color:#008000;font-weight:bold"></artifactId></span> |
| </span></span><span style="display:flex;"><span> <span style="color:#008000;font-weight:bold"><version></span>...<span style="color:#008000;font-weight:bold"></version></span> |
| </span></span><span style="display:flex;"><span><span style="color:#008000;font-weight:bold"></dependency></span> |
| </span></span></code></pre></div><h2 id="features">Features</h2> |
| <p>This implementation follows the 11-052r4 OGC GeoSPARQL standard |
| (<a href="https://www.ogc.org/standards/geosparql">https://www.ogc.org/standards/geosparql</a>). The implementation is pure Java |
| and does not require any set-up or configuration of any third party relational |
| databases and geospatial extensions.</p> |
| <p>It implements the six Conformance Classes described in the GeoSPARQL document:</p> |
| <ul> |
| <li>Core</li> |
| <li>Topology Vocabulary</li> |
| <li>Geometry Extension</li> |
| <li>Geometry Topology</li> |
| <li>RDFS Entailment Extension</li> |
| <li>Query Rewrite Extension</li> |
| </ul> |
| <p>The WKT (as described in 11-052r4) and GML 2.0 Simple Features Profile |
| (10-100r3) serialisations are supported. Additional serialisations can be |
| implemented by extending the |
| <code>org.apache.jena.geosparql.implementation.datatype.GeometryDatatype</code> and |
| registering with Jena’s <code>org.apache.jena.datatypes.TypeMapper</code>.</p> |
| <p>All three spatial relation families are supported: <em>Simple Feature</em>, <em>Egenhofer</em> and <em>RCC8</em>.</p> |
| <p>Indexing and caching of spatial objects and relations is performed <em>on-demand</em> |
| during query execution. Therefore, set-up delays should be minimal. Spatial |
| indexing is available based on the <em>STRtree</em> from the JTS library. The <em>STRtree</em> |
| is readonly once built and contributions of a <em>QuadTree</em> implementation are |
| welcome.</p> |
| <p>Benchmarking of the implementation against Strabon and Parliament has found it |
| to be comparable or quicker. The benchmarking used was the Geographical query |
| and dataset (<a href="http://geographica.di.uoa.gr/">http://geographica.di.uoa.gr/</a>).</p> |
| <h2 id="additional-features">Additional Features</h2> |
| <p>The following additional features are also provided:</p> |
| <ul> |
| <li>Geometry properties are automatically calculated and do not need to be asserted in the dataset.</li> |
| <li>Conversion between EPSG spatial/coordinate reference systems is applied |
| automatically. Therefore, mixed datasets or querying can be applied. This is |
| reliance upon local installation of Apache SIS EPSG dataset, see <strong>Key |
| Dependencies</strong>.</li> |
| <li>Units of measure are automatically converted to the appropriate units for the |
| coordinate reference system.</li> |
| <li>Geometry, transformation and spatial relation results are stored in persistent |
| and configurable time-limited caches to improve response times and reduce |
| recalculations.</li> |
| <li>Dataset conversion between serialisations and spatial/coordinate reference |
| systems. Tabular data can also be loaded, see RDF Tables project |
| (<a href="https://github.com/galbiston/rdf-tables">https://github.com/galbiston/rdf-tables</a>).</li> |
| <li>Functions to test Geometry properties directly on Geometry Literals have been included for convenience.</li> |
| </ul> |
| <h3 id="sparql-query-configuration">SPARQL Query Configuration</h3> |
| <p>Using the library for SPARQL querying requires one line of code. All indexing |
| and caching is performed during query execution and so there should be minimal |
| delay during initialisation. This will register the Property Functions with ARQ |
| query engine and configures the <em>indexes</em> used for time-limited caching.</p> |
| <p>There are three <em>indexes</em> which can be configured independently or switched off. |
| These <em>indexes</em> retain data that may be required again when a query is being |
| executed but may not be required between different queries. Therefore, the |
| memory usage will grow during query execution and then recede as data is not |
| re-used. All the <em>indexes</em> support concurrency and can be set to a maximum size |
| or allowed to increase capacity as required.</p> |
| <ul> |
| <li><em>Geometry Literal</em>: Geometry objects following de-serialisation from <code>Geometry Literal</code>.</li> |
| <li><em>Geometry Transform</em>: Geometry objects resulting from coordinate transformations between spatial reference systems.</li> |
| <li><em>Query Rewrite</em>: results of spatial relations between <code>Feature</code> and <code>Geometry</code> spatial objects.</li> |
| </ul> |
| <p>Testing has found up to 20% improvement in query completion durations using the indexes. |
| The <em>indexes</em> can be configured by size, retention duration and frequency of clean up.</p> |
| <ul> |
| <li> |
| <p>Basic setup with default values: <code>GeoSPARQLConfig.setupMemoryIndex()</code></p> |
| </li> |
| <li> |
| <p>Indexes set to maximum sizes: <code>GeoSPARQLConfig.setupMemoryIndexSize(50000, 50000, 50000)</code></p> |
| </li> |
| <li> |
| <p>Indexes set to remove objects not used after 5 seconds: <code>GeoSPARQLConfig.setupMemoryIndexExpiry(5000, 5000, 5000)</code></p> |
| </li> |
| <li> |
| <p>No indexes setup (Query rewrite still performed but results not stored) : <code>GeoSPARQLConfig.setupNoIndex()</code></p> |
| </li> |
| <li> |
| <p>No indexes and no query rewriting: <code>GeoSPARQLConfig.setupNoIndex(false)</code></p> |
| </li> |
| <li> |
| <p>Reset indexes and other stored data: <code>GeoSPARQLConfig.reset()</code></p> |
| </li> |
| </ul> |
| <p>A variety of configuration methods are provided in |
| <code>org.apache.jena.geosparql.configuration.GeoSPARQLConfig</code>. Caching of |
| frequently used but small quantity data is also applied in several <em>registries</em>, |
| e.g. coordinate reference systems and mathematical transformations.</p> |
| <p>Example GeoSPARQL query:</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sparql" data-lang="sparql"><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">PREFIX</span> <span style="color:#00f;font-weight:bold">geo</span>: <span style="color:#a0a000"><http://www.opengis.net/ont/geosparql#></span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">SELECT</span> <span style="color:#b8860b">?obj</span> |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">WHERE</span>{ |
| </span></span><span style="display:flex;"><span> <span style="color:#b8860b">?subj</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">sfContains</span> <span style="color:#b8860b">?obj</span> |
| </span></span><span style="display:flex;"><span>} <span style="color:#a2f;font-weight:bold">ORDER by</span> <span style="color:#b8860b">?obj</span> |
| </span></span></code></pre></div><h3 id="querying-datasets--models-with-sparql">Querying Datasets & Models with SPARQL</h3> |
| <p>The setup of GeoSPARQL Jena only needs to be performed once in an application. |
| After it is set up querying is performed using Jena’s standard query methods.</p> |
| <p>To query a Model with GeoSPARQL or standard SPARQL:</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>GeoSPARQLConfig<span style="color:#666">.</span><span style="color:#b44">setupMemoryIndex</span><span style="color:#666">();</span> |
| </span></span><span style="display:flex;"><span>Model model <span style="color:#666">=</span> <span style="color:#666">.....;</span> |
| </span></span><span style="display:flex;"><span>String query <span style="color:#666">=</span> <span style="color:#666">....;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">try</span> <span style="color:#666">(</span>QueryExecution qe <span style="color:#666">=</span> QueryExecution<span style="color:#666">.</span><span style="color:#b44">create</span><span style="color:#666">(</span>query<span style="color:#666">,</span> model<span style="color:#666">))</span> <span style="color:#666">{</span> |
| </span></span><span style="display:flex;"><span> ResultSet rs <span style="color:#666">=</span> qe<span style="color:#666">.</span><span style="color:#b44">execSelect</span><span style="color:#666">();</span> |
| </span></span><span style="display:flex;"><span> ResultSetFormatter<span style="color:#666">.</span><span style="color:#b44">outputAsTSV</span><span style="color:#666">(</span>rs<span style="color:#666">);</span> |
| </span></span><span style="display:flex;"><span><span style="color:#666">}</span> |
| </span></span></code></pre></div><p>If your dataset needs to be separate from your application and accessed over |
| HTTP then you probably need the <a href="geosparql-assembler">GeoSPARQL Assembler</a> to |
| integrate with Fuseki. The GeoSPARQL functionality needs to be setup in the |
| application or Fuseki server where the dataset is located.</p> |
| <p>It is <strong>recommended</strong> that <code>hasDefaultGeometry</code> properties are included in the |
| dataset to access all functionality. It is <strong>necessary</strong> that <code>SpatialObject</code> |
| classes are asserted or inferred (i.e. a reasoner with the GeoSPARQL schema is |
| applied) in the dataset. Methods to prepare a dataset can be found in |
| <code>org.apache.jena.geosparql.configuration.GeoSPARQLOperations</code>.</p> |
| <h3 id="api-the-library-can-be-used-as-an-api-in-java--the-main-class-to-handle">API The library can be used as an API in Java. The main class to handle</h3> |
| <p>geometries and their spatial relations is the <code>GeometryWrapper</code>. This can be |
| obtained by parsing the string representation of a geometry using the |
| appropriate datatype (e.g. WKT or GML). Alternatively, a Literal can be |
| extracted automatically using the <code>GeometryWrapper.extract()</code> method and |
| registered datatypes. The <code>GeometryWrapperFactory</code> can be used to directly |
| construct a <code>GeometryWrapper</code>. There is overlap between spatial relation |
| families so repeated methods are not specified.</p> |
| <ul> |
| <li> |
| <p>Parse a <code>Geometry Literal</code>: <code>GeometryWrapper geometryWrapper = WKTDatatype.INSTANCE.parse("POINT(1 1)");</code></p> |
| </li> |
| <li> |
| <p>Extract from a Jena Literal: <code>GeometryWrapper geometryWrapper = GeometryWrapper.extract(geometryLiteral);</code></p> |
| </li> |
| <li> |
| <p>Create from a JTS Geometry: <code>GeometryWrapper geometryWrapper = GeometryWrapperFactory.createGeometry(geometry, srsURI, geometryDatatypeURI);</code></p> |
| </li> |
| <li> |
| <p>Create from a JTS Point Geometry: <code>GeometryWrapper geometryWrapper = GeometryWrapperFactory.createPoint(coordinate, srsURI, geometryDatatypeURI);</code></p> |
| </li> |
| <li> |
| <p>Convert CRS/SRS: <code>GeometryWrapper otherGeometryWrapper = geometryWrapper.convertCRS("http://www.opengis.net/def/crs/EPSG/0/27700")</code></p> |
| </li> |
| <li> |
| <p>Spatial Relation: <code>boolean isCrossing = geometryWrapper.crosses(otherGeometryWrapper);</code></p> |
| </li> |
| <li> |
| <p>DE-9IM Intersection Pattern: <code>boolean isRelated = geometryWrapper.relate(otherGeometryWrapper, "TFFFTFFFT");</code></p> |
| </li> |
| <li> |
| <p>Geometry Property: <code>boolean isEmpty = geometryWrapper.isEmpty();</code></p> |
| </li> |
| </ul> |
| <p>The GeoSPARQL standard specifies that WKT Geometry Literals without an SRS URI are defaulted to CRS84 <code>http://www.opengis.net/def/crs/OGC/1.3/CRS84</code>.</p> |
| <h2 id="key-dependencies">Key Dependencies</h2> |
| <h3 id="geosparql">GeoSPARQL</h3> |
| <p>The OGC GeoSPARQL standard supports representing and querying geospatial data on |
| the Semantic Web. GeoSPARQL defines a vocabulary for representing geospatial |
| data in RDF, and it defines an extension to the SPARQL query language for |
| processing geospatial data. In addition, GeoSPARQL is designed to accommodate |
| systems based on qualitative spatial reasoning and systems based on quantitative |
| spatial computations.</p> |
| <p>The GeoSPARQL standard is based upon the OGC Simple Features standard |
| (<a href="http://www.opengeospatial.org/standards/sfa">http://www.opengeospatial.org/standards/sfa</a>) used in relational databases. |
| Modifications and enhancements have been made for usage with RDF and SPARQL. |
| The Simple Features standard, and by extension GeoSPARQL, simplify calculations |
| to Euclidean planer geometry. Therefore, datasets using a geographic |
| spatial/coordinate reference system, which are based on latitude and longitude |
| on an ellipsoid, e.g. WGS84, will have minor error introduced. This error has |
| been deemed acceptable due to the simplification in calculation it offers.</p> |
| <h3 id="apache-sissis_data-environment-variable">Apache SIS/SIS_DATA Environment Variable</h3> |
| <p>Apache Spatial Information System (SIS) is a free software, Java language |
| library for developing geospatial applications. SIS provides data structures |
| for geographic features and associated meta-data along with methods to |
| manipulate those data structures. The library is an implementation of GeoAPI |
| 3.0 interfaces and can be used for desktop or server applications.</p> |
| <p>A subset of the EPSG spatial/coordinate reference systems are included by default. |
| The full EPSG dataset is not distributed due to the EPSG terms of use being incompatible with the Apache Licence. |
| Several options are available to include the EPSG dataset by setting the <code>SIS_DATA</code> environment variable (<a href="http://sis.apache.org/epsg.html">http://sis.apache.org/epsg.html</a>).</p> |
| <p>An embedded EPSG dataset can be included in a Gradle application by adding the following dependency to <code>build.gradle</code>:</p> |
| <pre><code>ext.sisVersion = "1.1" |
| implementation "org.apache.sis.non-free:sis-embedded-data:$sisVersion" |
| </code></pre> |
| <h3 id="java-topology-suite">Java Topology Suite</h3> |
| <p>The JTS Topology Suite is a Java library for creating and manipulating vector geometry.</p> |
| <h2 id="note">Note</h2> |
| <p>The following are implementation points that may be useful during usage.</p> |
| <h3 id="geosparql-schema">GeoSPARQL Schema</h3> |
| <p>An RDF/XML schema has been published for the GeoSPARQL v1.0 standard (v1.0.1 - |
| <a href="http://schemas.opengis.net/geosparql/1.0/geosparql_vocab_all.rdf">http://schemas.opengis.net/geosparql/1.0/geosparql_vocab_all.rdf</a>). This can |
| be applied to Jena Models (see <a href="/documentation/inference/">the inference |
| documentation</a>) to provide RDFS and OWL inferencing |
| on a GeoSPARQL conforming dataset. However, the published schema does not |
| conform with the standard.</p> |
| <p>The property <code>hasDefaultGeometry</code> is missing from the schema and instead the |
| <code>defaultGeometry</code> property is stated.</p> |
| <p>This prevents RDFS inferencing being performed correctly and has been reported |
| to the OGC Standards Tracker. A corrected version of the schema is available in |
| the <code>Resources</code> folder.</p> |
| <h3 id="spatial-relations">Spatial Relations</h3> |
| <p>The GeoSPARQL and Simple Features standard both define the DE-9IM intersection |
| patterns for the three spatial relation families. However, these patterns are |
| not always consistent with the patterns stated by the JTS library for certain |
| relations.</p> |
| <p>For example, GeoSPARQL/Simple Features use <code>TFFFTFFFT</code> <em>equals</em> relations in |
| <em>Simple Feature</em>, <em>Egenhofer</em> and <em>RCC8</em>. However, this does not yield the |
| usually expected result when comparing a pair of point geometries. The Simple |
| Features standard states that the boundary of a point is empty. Therefore, the |
| boundary intersection of two points would also be empty so give a negative |
| comparison result.</p> |
| <p>JTS, and other libraries, use the alternative intersection pattern of |
| <code>T*F**FFF*</code>. This is a combination of the <em>within</em> and <em>contains</em> relations and |
| yields the expected results for all geometry types.</p> |
| <p>The spatial relations utilised by JTS have been implemented as the extension |
| <code>spatial:equals</code> filter and property functions. A user can also supply their |
| own DE-9IM intersection patterns by using the <code>geof:relate</code> filter function.</p> |
| <h3 id="spatial-relations-and-geometry-shapestypes">Spatial Relations and Geometry Shapes/Types</h3> |
| <p>The spatial relations for the three spatial families do not apply to all |
| combinations of the geometry shapes (<code>Point</code>, <code>LineString</code>, <code>Polygon</code>) and their |
| collections (<code>MultiPoint</code>, <code>MultiLineString</code>, <code>MultiPolygon</code>). Therefore, some |
| queries may not produce all the results that may initially be expected.</p> |
| <p>Some examples are:</p> |
| <ul> |
| <li>In some relations there may only be results when a collection of shapes is being used, e.g. two multi-points can overlap but two points cannot.</li> |
| <li>A relation may only apply for one combination but not its reciprocal, e.g. a line may cross a polygon but a polygon may not cross a line.</li> |
| <li>The <em>RCC8</em> family only applies to <code>Polygon</code> and <code>MultiPolygon</code> types.</li> |
| </ul> |
| <p>Refer to pages 8-10 of 11-052r4 GeoSPARQL standard for more details.</p> |
| <h3 id="equals-relations">Equals Relations</h3> |
| <p>The three equals relations (<em>sfEquals</em>, <em>ehEquals</em> and <em>rccEquals</em>) use spatial |
| equality and not lexical equality. Therefore, some comparisons using these |
| relations may not be as expected.</p> |
| <p>The JTS description of <em>sfEquals</em> is:</p> |
| <ul> |
| <li>True if two geometries have at least one point in common and no point of either geometry lies in the exterior of the other geometry.</li> |
| </ul> |
| <p>Therefore, two empty geometries will return false as they are not spatially equal. |
| Shapes which differ in the number of points but have the same geometry are equal and will return true.</p> |
| <p>e.g. <code>LINESTRING (0 0, 0 10)</code> and <code>LINESTRING (0 0, 0 5, 0 10)</code> are spatially equal.</p> |
| <h3 id="query-rewrite-extension">Query Rewrite Extension</h3> |
| <p>The Query Rewrite Extension provides for simpler querying syntax. <code>Feature</code> and |
| <code>Geometry</code> can be used in spatial relations without needing the relations to be |
| asserted in the dataset. This also means the <code>Geometry Literal</code> does not need |
| to be specified in the query. In the case of <code>Features</code> this requires the |
| <code>hasDefaultGeometry</code> property to be used in the dataset.</p> |
| <p>This means the query:</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sparql" data-lang="sparql"><span style="display:flex;"><span><span style="color:#b8860b">?subj</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">hasDefaultGeometry</span> <span style="color:#b8860b">?subjGeom</span> . |
| </span></span><span style="display:flex;"><span><span style="color:#b8860b">?subjGeom</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">hasSerialization</span> <span style="color:#b8860b">?subjLit</span> . |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#b8860b">?obj</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">hasDefaultGeometry</span> <span style="color:#b8860b">?objGeom</span> . |
| </span></span><span style="display:flex;"><span><span style="color:#b8860b">?objGeom</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">hasSerialization</span> <span style="color:#b8860b">?objLit</span> . |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">FILTER</span>(<span style="color:#00f;font-weight:bold">geof</span>:<span style="color:#008000;font-weight:bold">sfContains</span>(<span style="color:#b8860b">?subjLit</span>, <span style="color:#b8860b">?objLit</span>)) |
| </span></span></code></pre></div><p>becomes:</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sparql" data-lang="sparql"><span style="display:flex;"><span><span style="color:#b8860b">?subj</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">sfContains</span> <span style="color:#b8860b">?obj</span> . |
| </span></span></code></pre></div><p>Methods are available to apply the <code>hasDefaultGeometry</code> property to every |
| <code>Geometry</code> with a single <code>hasGeometry</code> property, see |
| <code>org.apache.jena.geosparql.configuration.GeoSPARQLOperations</code>.</p> |
| <p>Depending upon the spatial relation, queries may include the specified <code>Feature</code> |
| and <code>Geometry</code> in the results. e.g. FeatureA is bound in a query on a dataset |
| only containing FeatureA and GeometryA. The results FeatureA and GeometryA are |
| returned rather than no results. Therefore, filtering using |
| <code>FILTER(!sameTerm(?subj, ?obj))</code> etc. may be needed in some cases. The query |
| rewrite functionality can be switched off in the library configuration, see |
| <code>org.apache.jena.geosparql.configuration.GeoSPARQLConfig</code>.</p> |
| <p>Each dataset is assigned a Query Rewrite Index to store the results of previous |
| tests. There is the potential that relations are tested multiple times in a |
| query (i.e. <em>Feature-Feature</em>, <em>Feature-Geometry</em>, <em>Geometry-Geometry</em>, |
| <em>Geometry-Feature</em>). Therefore, it is useful to retain the results for at least |
| a short period of time.</p> |
| <p>Iterating through all combinations of spatial relations for a dataset containing |
| <em>n</em> Geometry Literals will produce 27<em>n</em>^2 true/false results (asserting the |
| true result statements in a dataset would be a subset). Control is given on a |
| dataset basis to allow choice in when and how storage of rewrite results is |
| applied, e.g. store all found results on a small dataset but on demand for a |
| large dataset.</p> |
| <p>This index can be configured on a global and individual dataset basis for the |
| maximum size and duration until unused items are removed. Query rewriting can |
| be switched on independently of the indexes, i.e. query rewriting can be |
| performed but an index is configured to not store the result.</p> |
| <p>As an extension to the standard, supplying a <code>Geometry Literal</code> is |
| also permitted. For example:</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sparql" data-lang="sparql"><span style="display:flex;"><span><span style="color:#b8860b">?subj</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">sfContains</span> <span style="color:#b44">"POINT(0 0)"</span><span style="color:#666">^^</span><span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">wktLiteral</span> . |
| </span></span></code></pre></div><h3 id="dataset-conversion">Dataset Conversion</h3> |
| <p>Methods to convert datasets between serialisations and spatial/coordinate |
| reference systems are available in: |
| <code>org.apache.jena.geosparql..configuration.GeoSPARQLOperations</code></p> |
| <p>The following list shows some of the operations that can be performed. Once |
| these operations have been performed they can be serialised to file or stored in |
| a Jena TDB to remove the need to reprocess.</p> |
| <ul> |
| <li> |
| <p>Load a Jena Model from file: <code>Model dataModel = RDFDataMgr.loadModel("data.ttl");</code></p> |
| </li> |
| <li> |
| <p>Convert <code>Feature-GeometryLiteral</code> to the GeoSPARQL <code>Feature-Geometry-GeometryLiteral</code> structure: <code>Model geosparqlModel = GeoSPARQLOperations.convertGeometryStructure(dataModel);</code></p> |
| </li> |
| <li> |
| <p>Convert <code>Feature-Lat, Feature-Lon</code> Geo predicates to the GeoSPARQL <code>Feature-Geometry-GeometryLiteral</code> structure, with option to remove Geo predicates: <code>Model geosparqlModel = GeoSPARQLOperations.convertGeoPredicates(dataModel, true);</code></p> |
| </li> |
| <li> |
| <p>Assert additional <code>hasDefaultGeometry</code> statements for single <code>hasGeometry</code> triples, used in Query Rewriting: <code>GeoSPARQLOperations.applyDefaultGeometry(geosparqlModel);</code></p> |
| </li> |
| <li> |
| <p>Convert Geometry Literals to the WGS84 spatial reference system and WKT datatype: <code>Model model = GeoSPARQLOperations.convert(geosparqlModel, "http://www.opengis.net/def/crs/EPSG/0/4326", "http://www.opengis.net/ont/geosparql#wktLiteral");</code></p> |
| </li> |
| <li> |
| <p>Apply GeoSPARQL schema with RDFS inferencing and assert additional statements in the Model: <code>GeoSPARQLOperations.applyInferencing(model);</code></p> |
| </li> |
| <li> |
| <p>Apply commonly used GeoSPARQL prefixes for URIs to the model: <code>GeoSPARQLOperations.applyPrefixes(model);</code></p> |
| </li> |
| <li> |
| <p>Create Spatial Index for a Model within a Dataset for spatial querying: <code>Dataset dataset = SpatialIndex.wrapModel(model);</code></p> |
| </li> |
| </ul> |
| <p>Other operations are available and can be applied to a Dataset containing |
| multiple Models and in some cases files and folders. These operations do |
| <strong>not</strong> configure and set up the GeoSPARQL functions or indexes that are required |
| for querying.</p> |
| <h3 id="spatial-index">Spatial Index</h3> |
| <p>A Spatial Index can be created to improve searching of a dataset. The Spatial |
| Index is expected to be unique to the dataset and should not be shared between |
| datasets. Once built the Spatial Index cannot have additional items added to |
| it.</p> |
| <p>A Spatial Index is required for the <code>jena-spatial</code> property functions and is |
| optional for the GeoSPARQL spatial relations. Only a single SRS can be used for |
| a Spatial Index, and it is recommended that datasets are converted to a single |
| SRS, see <code>GeoSPARQLOperations</code>.</p> |
| <p>Setting up a Spatial Index can be done through |
| <code>org.apache.jena.geosparql.configuration.GeoSPARQLConfig</code>. Additional methods |
| for building, loading and saving Spatial Indexes are provided in |
| <code>org.apache.jena.geosparql.spatial.SpatialIndex</code>.</p> |
| <h3 id="units-uri">Units URI</h3> |
| <p>Spatial/coordinate reference systems use a variety of measuring systems for |
| defining distances. These can be specified using a URI identifier, as either |
| URL or URN, with conversion undertaken automatically as required. It should be |
| noted that there is error inherent in spatial reference systems and some |
| variation in values may occur between different systems.</p> |
| <p>The following table gives some examples of units that are supported (additional |
| units can be added to the <code>UnitsRegistry</code> using the <code>javax.measure.Unit</code> API). |
| These URIs are all in the namespace <code>http://www.opengis.net/def/uom/OGC/1.0/</code> and |
| here use the prefix <code>units</code>.</p> |
| <table> |
| <thead> |
| <tr> |
| <th>URI</th> |
| <th>Description</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>units:kilometre or units:kilometer</td> |
| <td>Kilometres</td> |
| </tr> |
| <tr> |
| <td>units:metre or units:meter</td> |
| <td>Metres</td> |
| </tr> |
| <tr> |
| <td>units:mile or units:statuteMile</td> |
| <td>Miles</td> |
| </tr> |
| <tr> |
| <td>units:degree</td> |
| <td>Degrees</td> |
| </tr> |
| <tr> |
| <td>units:radian</td> |
| <td>Radians</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>Full listing of default Units can be found in |
| <code>org.apache.jena.geosparql.implementation.vocabulary.Unit_URI</code>.</p> |
| <h2 id="geography-markup-language-support-gml">Geography Markup Language Support (GML)</h2> |
| <p>The supported GML profile is GML 2.0 Simple Features Profile (10-100r3), which |
| is a profile of GML 3.2.1 (07-036r1). The profile restricts the geometry shapes |
| permitted in GML 3.2.1 to a subset, see 10-100r3 page 22. The profile supports |
| Points, LineString and Polygon shapes used in WKT. There are also additional |
| shape serialisations available in the profile that do not exist in WKT or JTS to |
| provide simplified representations which would otherwise use LineStrings or |
| Polygons. Curves can be described by LineStringSegment, Arc, Circle and |
| CircleByCenterPoint. Surfaces can be formed similarly to Polygons or using |
| Curves. These additional shapes can be read as part of a dataset or query but |
| will not be produced if the SRS of the shape is transformed, instead a |
| LineString or Polygon representation will be produced.</p> |
| <p>Details of the GML structure for these shapes can be found in the |
| <a href="http://www.datypic.com/sc/niem21/s-geometryPrimitives.xsd.html">geometryPrimitives.xsd</a>, |
| <a href="http://www.datypic.com/sc/niem21/s-geometryBasic0d1d.xsd.html">geometryBasic0d1d.xsd</a>, |
| <a href="http://www.datypic.com/sc/niem21/s-geometryBasic2d.xsd.html">geometryBasic2d.xsd</a> |
| and |
| <a href="http://www.datypic.com/sc/niem21/s-geometryAggregates.xsd.html">geometryAggregates.xsd</a> |
| schemas.</p> |
| <p>The labelling of collections is as follows:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>Collection</th> |
| <th>Geometry</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>MultiPoint</td> |
| <td>Point</td> |
| </tr> |
| <tr> |
| <td>MultiCurve</td> |
| <td>LineString, Curve</td> |
| </tr> |
| <tr> |
| <td>MultiSurface</td> |
| <td>Polygon, Surface</td> |
| </tr> |
| <tr> |
| <td>MultiGeometry</td> |
| <td>Point, LineString, Curve, Polygon, Surface</td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="apache-jena-spatial-functionswgs84-geo-predicates">Apache Jena Spatial Functions/WGS84 Geo Predicates</h2> |
| <p>The <code>jena-spatial</code> module contains several SPARQL functions for querying |
| datasets using the WGS84 Geo predicates for latitude |
| (<code>http://www.w3.org/2003/01/geo/wgs84_pos#lat</code>) and longitude |
| (<code>http://www.w3.org/2003/01/geo/wgs84_pos#long</code>). These <code>jena-spatial</code> |
| functions are supported for both Geo predicates and Geometry Literals, i.e. a |
| GeoSPARQL dataset. Additional SPARQL filter functions have been provided to |
| convert Geo predicate properties into WKT strings and calculate Great Circle and |
| Euclidean distances. The <code>jena-spatial</code>functions require setting up a Spatial |
| Index for the target Dataset, |
| e.g. <code>GeoSPARQLConfig.setupSpatialIndex(dataset);</code>, see Spatial Index section.</p> |
| <h3 id="supported-features">Supported Features</h3> |
| <p>The Geo predicate form of spatial representation is restricted to only ‘Point’ |
| shapes in the WGS84 spatial/coordinate reference system. The Geo predicates are |
| properties of the <code>Feature</code> and do not use the properties and structure of the |
| GeoSPARQL standard, including Geometry Literals. Methods are available to |
| convert datasets from Geo predicates to GeoSPARQL structure, see: |
| <code>org.apache.jena.geosparql.configuration.GeoSPARQLOperations</code></p> |
| <p>The spatial relations and query re-writing of GeoSPARQL outlined previously has been implemented for Geo predicates. |
| However, only certain spatial relations are valid for <code>Point</code> to <code>Point</code> relationships. |
| Refer to pages 8-10 of 11-052r4 GeoSPARQL standard for more details.</p> |
| <p>Geo predicates can be converted to Geometry Literals in query and then used with the GeoSPARQL filter functions.</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sparql" data-lang="sparql"><span style="display:flex;"><span><span style="color:#b8860b">?subj</span> <span style="color:#00f;font-weight:bold">wgs</span>:<span style="color:#008000;font-weight:bold">lat</span> <span style="color:#b8860b">?lat</span> . |
| </span></span><span style="display:flex;"><span><span style="color:#b8860b">?subj</span> <span style="color:#00f;font-weight:bold">wgs</span>:<span style="color:#008000;font-weight:bold">long</span> <span style="color:#b8860b">?lon</span> . |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">BIND</span>(<span style="color:#00f;font-weight:bold">spatialF</span>:<span style="color:#008000;font-weight:bold">convertLatLon</span>(<span style="color:#b8860b">?lat</span>, <span style="color:#b8860b">?lon</span>) <span style="color:#a2f;font-weight:bold">as</span> <span style="color:#b8860b">?point</span>) . |
| </span></span><span style="display:flex;"><span><span style="color:#080;font-style:italic">#Coordinate order is Lon/Lat without stated SRS URI.</span> |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">BIND</span>(<span style="color:#b44">"POLYGON((...))"</span><span style="color:#666">^^</span><span style="color:#a0a000"><http://www.opengis.net/ont/geosparql#wktLiteral></span> <span style="color:#a2f;font-weight:bold">AS</span> <span style="color:#b8860b">?box</span>) . |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">FILTER</span>(<span style="color:#00f;font-weight:bold">geof</span>:<span style="color:#008000;font-weight:bold">sfContains</span>(<span style="color:#b8860b">?box</span>, <span style="color:#b8860b">?point</span>)) |
| </span></span></code></pre></div><p>Alternatively, utilising more shapes, relations and spatial reference systems |
| can be achieved by converting the dataset to the GeoSPARQL structure.</p> |
| <div class="highlight"><pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sparql" data-lang="sparql"><span style="display:flex;"><span><span style="color:#b8860b">?subj</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">hasGeometry</span> <span style="color:#b8860b">?geom</span> . |
| </span></span><span style="display:flex;"><span><span style="color:#b8860b">?geom</span> <span style="color:#00f;font-weight:bold">geo</span>:<span style="color:#008000;font-weight:bold">hasSerialization</span> <span style="color:#b8860b">?geomLit</span> . |
| </span></span><span style="display:flex;"><span><span style="color:#080;font-style:italic">#Coordinate order is Lon/Lat without stated SRS URI.</span> |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">BIND</span>(<span style="color:#b44">"POLYGON((...))"</span><span style="color:#666">^^</span><span style="color:#a0a000"><http://www.opengis.net/ont/geosparql#wktLiteral></span> <span style="color:#a2f;font-weight:bold">AS</span> <span style="color:#b8860b">?box</span>) . |
| </span></span><span style="display:flex;"><span><span style="color:#a2f;font-weight:bold">FILTER</span>(<span style="color:#00f;font-weight:bold">geof</span>:<span style="color:#008000;font-weight:bold">sfContains</span>(<span style="color:#b8860b">?box</span>, <span style="color:#b8860b">?geomLit</span>)) |
| </span></span></code></pre></div><p>Datasets can contain both Geo predicates and Geometry Literals without |
| interference. However, a dataset containing both types will only examine those |
| <code>Features</code> which have Geometry Literals for spatial relations, i.e. the check |
| for Geo predicates is a fallback when Geometry Literals aren’t found. |
| Therefore, it is <strong>not</strong> recommended to insert new Geo predicate properties |
| after a dataset has been converted to GeoSPARQL structure (unless corresponding |
| Geometry and Geometry Literals are included).</p> |
| <h3 id="filter-functions">Filter Functions</h3> |
| <p>These filter functions are available in the |
| <code>http://jena.apache.org/function/spatial#</code> namespace and here use the prefix |
| <code>spatialF</code>.</p> |
| <table> |
| <thead> |
| <tr> |
| <th>Function Name</th> |
| <th>Description</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td><em>?wktString</em> <strong>spatialF:convertLatLon</strong>(<em>?lat</em>, <em>?lon</em>)</td> |
| <td>Converts Lat and Lon double values into WKT string of a Point with WGS84 SRS.</td> |
| </tr> |
| <tr> |
| <td><em>?wktString</em> <strong>spatialF:convertLatLonBox</strong>(<em>?latMin</em>, <em>?lonMin</em>, <em>?latMax</em>, <em>?lonMax</em>)</td> |
| <td>Converts Lat and Lon double values into WKT string of a Polygon forming a box with WGS84 SRS.</td> |
| </tr> |
| <tr> |
| <td><em>?boolean</em> <strong>spatialF:equals</strong>(<em>?geomLit1</em>, <em>?geomLit2</em>)</td> |
| <td>True, if <em>geomLit1</em> is spatially equal to <em>geomLit2</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?boolean</em> <strong>spatialF:nearby</strong>(<em>?geomLit1</em>, <em>?geomLit2</em>, <em>?distance</em>, <em>?unitsURI</em>)</td> |
| <td>True, if <em>geomLit1</em> is within <em>distance</em> of <em>geomLit2</em> using the distance <em>units</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?boolean</em> <strong>spatialF:withinCircle</strong>(<em>?geomLit1</em>, <em>?geomLit2</em>, <em>?distance</em>, <em>?unitsURI</em>)</td> |
| <td>True, if <em>geomLit1</em> is within <em>distance</em> of <em>geomLit2</em> using the distance <em>units</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?radians</em> <strong>spatialF:angle</strong>(<em>?x1</em>, <em>?y1</em>, <em>?x2</em>, <em>?y2</em>)</td> |
| <td>Angle clockwise from y-axis from Point(x1,y1) to Point (x2,y2) in 0 to 2Ï€ radians.</td> |
| </tr> |
| <tr> |
| <td><em>?degrees</em> <strong>spatialF:angleDeg</strong>(<em>?x</em>, <em>?y1</em>, <em>?x2</em>, <em>?y2</em>)</td> |
| <td>Angle clockwise from y-axis from Point(x1,y1) to Point (x2,y2) in 0 to 360 degrees.</td> |
| </tr> |
| <tr> |
| <td><em>?distance</em> <strong>spatialF:distance</strong>(<em>?geomLit1</em>, <em>?geomLit2</em>, <em>?unitsURI</em>)</td> |
| <td>Distance between two Geometry Literals in distance <em>units</em>. Chooses distance measure based on SRS type. Great Circle distance for Geographic SRS and Euclidean otherwise.</td> |
| </tr> |
| <tr> |
| <td><em>?radians</em> <strong>spatialF:azimuth</strong>(<em>?lat1</em>, <em>?lon1</em>, <em>?lat2</em>, <em>?lon2</em>)</td> |
| <td>Forward azimuth clockwise from North between two Lat/Lon Points in 0 to 2Ï€ radians.</td> |
| </tr> |
| <tr> |
| <td><em>?degrees</em> <strong>spatialF:azimuthDeg</strong>(<em>?lat1</em>, <em>?lon1</em>, <em>?lat2</em>, <em>?lon2</em>)</td> |
| <td>Forward azimuth clockwise from North between two Lat/Lon Points in 0 to 360 degrees.</td> |
| </tr> |
| <tr> |
| <td><em>?distance</em> <strong>spatialF:greatCircle</strong>(<em>?lat1</em>, <em>?lon1</em>, <em>?lat2</em>, <em>?lon2</em>, <em>?unitsURI</em>)</td> |
| <td>Great Circle distance (Vincenty formula) between two Lat/Lon Points in distance <em>units</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?distance</em> <strong>spatialF:greatCircleGeom</strong>(<em>?geomLit1</em>, <em>?geomLit2</em>, <em>?unitsURI</em>)</td> |
| <td>Great Circle distance (Vincenty formula) between two Geometry Literals in distance <em>units</em>. Use <code>http://www.opengis.net/def/function/geosparql/distance</code> from GeoSPARQL standard for Euclidean distance.</td> |
| </tr> |
| <tr> |
| <td><em>?geomLit2</em> <strong>spatialF:transform</strong>(<em>?geomLit1</em>, <em>?datatypeURI</em>, <em>?srsURI</em>)</td> |
| <td>Transform Geometry Literal by Datatype and SRS.</td> |
| </tr> |
| <tr> |
| <td><em>?geomLit2</em> <strong>spatialF:transformDatatype</strong>(<em>?geomLit1</em>, <em>?datatypeURI</em>)</td> |
| <td>Transform Geometry Literal by Datatype.</td> |
| </tr> |
| <tr> |
| <td><em>?geomLit2</em> <strong>spatialF:transformSRS</strong>(<em>?geomLit1</em>, <em>?srsURI</em>)</td> |
| <td>Transform Geometry Literal by SRS.</td> |
| </tr> |
| </tbody> |
| </table> |
| <h3 id="property-functions">Property Functions</h3> |
| <p>These property functions are available in the <code>http://jena.apache.org/spatial#</code> |
| namespace and here use the prefix <code>spatial</code>. This is the same namespace as the |
| <code>jena-spatial</code> functions utilise and these form direct replacements. The |
| subject <code>Feature</code> may be bound, to test the pattern is true, or unbound, to find |
| all cases the pattern is true. These property functions require a <code>Spatial Index</code> to be setup for the dataset.</p> |
| <p>The optional <em>?limit</em> parameter restricts the number of results returned. The |
| default value is -1 which returns all results. No guarantee is given for |
| ordering of results. The optional <em>?unitsURI</em> parameter specifies the units of |
| a distance. The default value is kilometres through the string or resource |
| <code>http://www.opengis.net/def/uom/OGC/1.0/kilometre</code>.</p> |
| <p>The <code>spatial:equals</code> property function behaves the same way as the main |
| GeoSPARQL property functions. Either, both or neither of the subject and object |
| can be bound. A <code>Spatial Index</code> is <strong>not</strong> required for the dataset with the |
| <code>spatial:equals</code> property function.</p> |
| <table> |
| <thead> |
| <tr> |
| <th>Function Name</th> |
| <th>Description</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td><em>?spatialObject1</em> <strong>spatial:equals</strong> <em>?spatialObject2</em></td> |
| <td>Find <em>spatialObjects</em> (i.e. <em>features</em> or <em>geometries</em>) that are spatially equal.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:intersectBox</strong>(<em>?latMin</em> <em>?lonMin</em> <em>?latMax</em> <em>?lonMax</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that intersect the provided box, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:intersectBoxGeom</strong>(<em>?geomLit1</em> <em>?geomLit2</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that intersect the provided box, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:withinBox</strong>(<em>?latMin</em> <em>?lonMin</em> <em>?latMax</em> <em>?lonMax</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that intersect the provided box, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:withinBoxGeom</strong>(<em>?geomLit1</em> <em>?geomLit2</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are within the provided box, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:nearby</strong>(<em>?lat</em> <em>?lon</em> <em>?radius</em> [ <em>?unitsURI</em> [ <em>?limit</em>]])</td> |
| <td>Find <em>features</em> that are within <em>radius</em> of the <em>distance</em> units, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:nearbyGeom</strong>(<em>?geomLit</em> <em>?radius</em> [ <em>?unitsURI</em> [ <em>?limit</em>]])</td> |
| <td>Find <em>features</em> that are within <em>radius</em> of the <em>distance</em> units, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:withinCircle</strong>(<em>?lat</em> <em>?lon</em> <em>?radius</em> [ <em>?unitsURI</em> [ <em>?limit</em>]])</td> |
| <td>Find <em>features</em> that are within <em>radius</em> of the <em>distance</em> units, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:withinCircleGeom</strong>(<em>?geomLit</em> <em>?radius</em> [ <em>?unitsURI</em> [ <em>?limit</em>]])</td> |
| <td>Find <em>features</em> that are within <em>radius</em> of the <em>distance</em> units, up to the <em>limit</em>.</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>The Cardinal Functions find all <code>Features</code> that are present in the specified |
| direction. In Geographic spatial reference systems (SRS), e.g. WGS84 and CRS84, |
| the East/West directions wrap around. Therefore, a search is made from the |
| shape’s edge for up to half the range of the SRS (i.e. 180 degrees in WGS84) and |
| will continue across the East/West boundary if necessary. In other SRS, |
| e.g. Projected onto a flat plane, the East/West check is made from the shape’s |
| edge to the farthest limit of the SRS range, i.e. there is no wrap around.</p> |
| <table> |
| <thead> |
| <tr> |
| <th>Cardinal Function Name</th> |
| <th>Description</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:north</strong>(<em>?lat</em> <em>?lon</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are North of the Lat/Lon point (point to +90 degrees), up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:northGeom</strong>(<em>?geomLit</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are North of the Geometry Literal, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:south</strong>(<em>?lat</em> <em>?lon</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are South of the Lat/Lon point (point to -90 degrees), up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:southGeom</strong>(<em>?geomLit</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are South of the Geometry Literal, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:east</strong>(<em>?lat</em> <em>?lon</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are East of the Lat/Lon point (point plus 180 degrees longitude, wrapping round), up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:eastGeom</strong>(<em>?geomLit</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are East of the Geometry Literal, up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:west</strong>(<em>?lat</em> <em>?lon</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are West of the Lat/Lon point (point minus 180 degrees longitude, wrapping round), up to the <em>limit</em>.</td> |
| </tr> |
| <tr> |
| <td><em>?feature</em> <strong>spatial:westGeom</strong>(<em>?geomLit</em> [ <em>?limit</em>])</td> |
| <td>Find <em>features</em> that are West of the Geometry Literal, up to the <em>limit</em>.</td> |
| </tr> |
| </tbody> |
| </table> |
| <h2 id="geometry-property-filter-functions">Geometry Property Filter Functions</h2> |
| <p>The GeoSPARQL standard provides a set of properties related to geometries, see |
| Section 8.4. These are applied on the Geometry resource and are automatically |
| determined if not asserted in the data. However, it may be necessary to |
| retrieve the properties of a Geometry Literal directly without an associated |
| Geometry resource. Filter functions to do this have been included as part of |
| the <code>http://www.opengis.net/def/function/geosparql/</code> namespace as a minor |
| variation to the GeoSPARQL standard. The relevant functions using the <code>geof</code> |
| prefix are:</p> |
| <table> |
| <thead> |
| <tr> |
| <th>Geometry Property Filter Function Name</th> |
| <th>Description</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td><em>?integer</em> <strong>geof:dimension</strong>(<em>?geometryLiteral</em>)</td> |
| <td>Topological dimension, e.g. 0 for Point, 1 for LineString and 2 for Polygon.</td> |
| </tr> |
| <tr> |
| <td><em>?integer</em> <strong>geof:coordinateDimension</strong>(<em>?geometryLiteral</em>)</td> |
| <td>Coordinate dimension, e.g. 2 for XY coordinates and 4 for XYZM coordinates.</td> |
| </tr> |
| <tr> |
| <td><em>?integer</em> <strong>geof:spatialDimension</strong>(<em>?geometryLiteral</em>)</td> |
| <td>Spatial dimension, e.g. 2 for XY coordinates and 3 for XYZM coordinates.</td> |
| </tr> |
| <tr> |
| <td><em>?boolean</em> <strong>geof:isEmpty</strong>(<em>?geometryLiteral</em>)</td> |
| <td>True, if geometry is empty.</td> |
| </tr> |
| <tr> |
| <td><em>?boolean</em> <strong>geof:isSimple</strong>(<em>?geometryLiteral</em>)</td> |
| <td>True, if geometry is simple.</td> |
| </tr> |
| <tr> |
| <td><em>?boolean</em> <strong>geof:isValid</strong>(<em>?geometryLiteral</em>)</td> |
| <td>True, if geometry is topologically valid.</td> |
| </tr> |
| </tbody> |
| </table> |
| <p>A dataset that follows the GeoSPARQL Feature-Geometry-GeometryLiteral can have |
| simpler SPARQL queries without needing to use these functions by taking |
| advantage of the Query Rewriting functionality. The <code>geof:isValid</code> filter |
| function and <code>geo:isValid</code> property for a Geometry resource are not part of the |
| GeoSPARQL standard but have been included as a minor variation.</p> |
| <h2 id="future-work">Future Work</h2> |
| <ul> |
| <li>Implementing GeoJSON as a <code>GeometryLiteral</code> serialisation (<a href="https://tools.ietf.org/html/rfc7946">https://tools.ietf.org/html/rfc7946</a>). |
| Producing GeoJSON is already possible with <strong>geof:asGeoJSON</strong>(<em>?geometryLiteral</em>).</li> |
| </ul> |
| <h2 id="contributors">Contributors</h2> |
| <p>The following individuals have made contributions to this project:</p> |
| <ul> |
| <li>Greg Albiston</li> |
| <li>Haozhe Chen</li> |
| <li>Taha Osman</li> |
| </ul> |
| <h2 id="why-use-this-implementation">Why Use This Implementation?</h2> |
| <p>There are several implementations of the GeoSPARQL standard. The conformance |
| and completeness of these implementations is difficult to ascertain and varies |
| between features.</p> |
| <p>However, the following may be of interest when considering whether to use this |
| implementation based on reviewing several alternatives.</p> |
| <table> |
| <thead> |
| <tr> |
| <th>This Implementation</th> |
| <th>Other Implementations</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>Implements all six components of the GeoSPARQL standard.</td> |
| <td>Generally partially implement the Geometry Topology and Geometry Extensions. Do not implement the Query Rewrite Extension.</td> |
| </tr> |
| <tr> |
| <td>Pure Java and does not require a supporting relational database. Configuration requires a single line of code (although Apache SIS may need some setting up, see above).</td> |
| <td>Require setting up a database, configuring a geospatial extension and setting environment variables.</td> |
| </tr> |
| <tr> |
| <td>Uses Jena, which conforms to the W3C standards for RDF and SPARQL. New versions of the standards will quickly feed through.</td> |
| <td>Not fully RDF and SPARQL compliant, e.g. RDFS/OWL inferencing or SPARQL syntax. Adding your own schema may not produce inferences.</td> |
| </tr> |
| <tr> |
| <td>Automatically determines geometry properties and handles mixed cases of units or coordinate reference systems. The GeoSPARQL standard suggests this approach but does not require it.</td> |
| <td>Tend to produce errors or no results in these situations.</td> |
| </tr> |
| <tr> |
| <td>Performs indexing and caching on-demand which reduces set-up time and only performs calculations that are required.</td> |
| <td>Perform indexing in the data loading phase and initialisation phase, which can lead to lengthy delays (even on relatively small datasets).</td> |
| </tr> |
| <tr> |
| <td>Uses JTS which does not truncate coordinate precision and applies spatial equality.</td> |
| <td>May truncate coordinate precision and apply lexical equality, which is quicker but does not comply with the GeoSPARQL standard.</td> |
| </tr> |
| </tbody> |
| </table> |
| |
| </article> |
| |
| <aside class="text-muted align-self-start mb-3 mb-xl-5 p-0 d-none d-xl-flex flex-column sticky-top"> |
| <h2 class="h6 sticky-top m-0 p-2 bg-body-tertiary">On this page</h2> |
| <nav id="TableOfContents"> |
| <ul> |
| <li><a href="#getting-started">Getting Started</a></li> |
| <li><a href="#features">Features</a></li> |
| <li><a href="#additional-features">Additional Features</a> |
| <ul> |
| <li><a href="#sparql-query-configuration">SPARQL Query Configuration</a></li> |
| <li><a href="#querying-datasets--models-with-sparql">Querying Datasets & Models with SPARQL</a></li> |
| <li><a href="#api-the-library-can-be-used-as-an-api-in-java--the-main-class-to-handle">API The library can be used as an API in Java. The main class to handle</a></li> |
| </ul> |
| </li> |
| <li><a href="#key-dependencies">Key Dependencies</a> |
| <ul> |
| <li><a href="#geosparql">GeoSPARQL</a></li> |
| <li><a href="#apache-sissis_data-environment-variable">Apache SIS/SIS_DATA Environment Variable</a></li> |
| <li><a href="#java-topology-suite">Java Topology Suite</a></li> |
| </ul> |
| </li> |
| <li><a href="#note">Note</a> |
| <ul> |
| <li><a href="#geosparql-schema">GeoSPARQL Schema</a></li> |
| <li><a href="#spatial-relations">Spatial Relations</a></li> |
| <li><a href="#spatial-relations-and-geometry-shapestypes">Spatial Relations and Geometry Shapes/Types</a></li> |
| <li><a href="#equals-relations">Equals Relations</a></li> |
| <li><a href="#query-rewrite-extension">Query Rewrite Extension</a></li> |
| <li><a href="#dataset-conversion">Dataset Conversion</a></li> |
| <li><a href="#spatial-index">Spatial Index</a></li> |
| <li><a href="#units-uri">Units URI</a></li> |
| </ul> |
| </li> |
| <li><a href="#geography-markup-language-support-gml">Geography Markup Language Support (GML)</a></li> |
| <li><a href="#apache-jena-spatial-functionswgs84-geo-predicates">Apache Jena Spatial Functions/WGS84 Geo Predicates</a> |
| <ul> |
| <li><a href="#supported-features">Supported Features</a></li> |
| <li><a href="#filter-functions">Filter Functions</a></li> |
| <li><a href="#property-functions">Property Functions</a></li> |
| </ul> |
| </li> |
| <li><a href="#geometry-property-filter-functions">Geometry Property Filter Functions</a></li> |
| <li><a href="#future-work">Future Work</a></li> |
| <li><a href="#contributors">Contributors</a></li> |
| <li><a href="#why-use-this-implementation">Why Use This Implementation?</a></li> |
| </ul> |
| </nav> |
| </aside> |
| </main> |
| |
| </div> |
| </div> |
| </div> |
| |
| <footer class="bd-footer py-4 py-md-5 mt-4 mt-lg-5 bg-body-tertiary"> |
| <div class="container" style="font-size:80%" > |
| <p> |
| Copyright © 2011–2024 The Apache Software Foundation, Licensed under the |
| <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>. |
| </p> |
| <p> |
| Apache Jena, Jena, the Apache Jena project logo, Apache and the Apache feather logos are trademarks of |
| The Apache Software Foundation. |
| <br/> |
| <a href="https://privacy.apache.org/policies/privacy-policy-public.html" |
| >Apache Software Foundation Privacy Policy</a>. |
| </p> |
| </div> |
| </footer> |
| |
| <script src="/js/popper.min.js.js" type="text/javascript"></script> |
| <script src="/js/bootstrap.min.js" type="text/javascript"></script> |
| <script src="/js/improve.js" type="text/javascript"></script> |
| |
| <script type="text/javascript"> |
| (function() { |
| 'use strict' |
| |
| |
| |
| const links = document.querySelectorAll(`a[href="${window.location.pathname}"]`) |
| if (links !== undefined && links !== null) { |
| for (const link of links) { |
| |
| link.classList.add('active') |
| let parentElement = link.parentElement |
| let count = 0 |
| const levelsLimit = 4 |
| |
| |
| |
| |
| |
| while (['UL', 'LI'].includes(parentElement.tagName) && count <= levelsLimit) { |
| if (parentElement.tagName === 'LI') { |
| |
| |
| |
| parentElement.querySelector('a:first-child').classList.add('active') |
| } |
| parentElement = parentElement.parentElement |
| count++ |
| } |
| } |
| } |
| })() |
| </script> |
| |
| </body> |
| </html> |