Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;

import org.apache.jena.datatypes.DatatypeFormatException;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.geosparql.implementation.GeometryWrapper;
import org.apache.jena.sparql.expr.ExprEvalException;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.function.FunctionBase1;

/** Implements geof:geometryType. */
public class GeometryTypeFF extends FunctionBase1 {

@Override
public NodeValue exec(NodeValue value) {
try {
GeometryWrapper geometry = GeometryWrapper.extract(value);
return NodeValue.makeNode(geometry.getGeometryTypeURI(), XSDDatatype.XSDanyURI);
} catch (DatatypeFormatException | IllegalArgumentException ex) {
throw new ExprEvalException(ex.getMessage(), ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;

import org.apache.jena.datatypes.DatatypeFormatException;
import org.apache.jena.geosparql.implementation.GeometryWrapper;
import org.apache.jena.sparql.expr.ExprEvalException;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.function.FunctionBase1;

/** Implements geof:is3D. */
public class Is3DFF extends FunctionBase1 {

@Override
public NodeValue exec(NodeValue value) {
try {
GeometryWrapper geometry = GeometryWrapper.extract(value);
return NodeValue.makeBoolean(geometry.is3D());
} catch (DatatypeFormatException ex) {
throw new ExprEvalException(ex.getMessage(), ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;

import org.apache.jena.datatypes.DatatypeFormatException;
import org.apache.jena.geosparql.implementation.GeometryWrapper;
import org.apache.jena.sparql.expr.ExprEvalException;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.function.FunctionBase1;

/** Implements geof:isMeasured. */
public class IsMeasuredFF extends FunctionBase1 {

@Override
public NodeValue exec(NodeValue value) {
try {
GeometryWrapper geometry = GeometryWrapper.extract(value);
return NodeValue.makeBoolean(geometry.isMeasured());
} catch (DatatypeFormatException ex) {
throw new ExprEvalException(ex.getMessage(), ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property;

import org.apache.jena.datatypes.DatatypeFormatException;
import org.apache.jena.geosparql.implementation.GeometryWrapper;
import org.apache.jena.sparql.expr.ExprEvalException;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.function.FunctionBase1;

/** Implements geof:numGeometries. */
public class NumGeometriesFF extends FunctionBase1 {

@Override
public NodeValue exec(NodeValue value) {
try {
GeometryWrapper geometry = GeometryWrapper.extract(value);
return NodeValue.makeInteger(geometry.getNumGeometries());
} catch (DatatypeFormatException ex) {
throw new ExprEvalException(ex.getMessage(), ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class GeometryWrapper implements Serializable {
private final String geometryDatatypeURI;
private GeometryDatatype geometryDatatype;
private String lexicalForm;
private final String sourceLexicalForm;
private String geometryTypeURI;
private String utmURI = null;
private Double latitude = null;

Expand All @@ -104,6 +106,26 @@ public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatype
this(geometry, GeometryReverse.check(geometry, srsURI.isEmpty() ? SRS_URI.DEFAULT_WKT_CRS84 : srsURI), srsURI.isEmpty() ? SRS_URI.DEFAULT_WKT_CRS84 : srsURI, geometryDatatypeURI, dimensionInfo, geometryLiteral);
}

/**
* Constructs a wrapper for the supplied geometry, with optional serialized
* text and an optional geometry subtype URI.
*
* @param geometry In X/Y or Y/X coordinate order of the SRS URI.
* @param srsURI The spatial reference system URI; an empty string uses CRS84.
* @param geometryDatatypeURI The geometry serialization datatype URI.
* @param dimensionInfo The geometry's coordinate layout and spatial and topological dimensions.
* @param geometryLiteral The serialized text representing this geometry, such as WKT or GML,
* in the format identified by geometryDatatypeURI. If null, the text
* is generated from the supplied geometry when serialization is requested.
* @param geometryTypeURI The subtype URI for this geometry in the specified datatype.
* If null, the datatype determines it when getGeometryTypeURI() is first called.
*/
public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo,
String geometryLiteral, String geometryTypeURI) {
this(geometry, srsURI, geometryDatatypeURI, dimensionInfo, geometryLiteral);
this.geometryTypeURI = geometryTypeURI;
}

protected GeometryWrapper(Geometry parsingGeometry, Geometry xyGeometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo) {
this(parsingGeometry, xyGeometry, srsURI, geometryDatatypeURI, dimensionInfo, null);
}
Expand All @@ -126,6 +148,7 @@ protected GeometryWrapper(Geometry parsingGeometry, Geometry xyGeometry, String

this.dimensionInfo = dimensionInfo;
this.lexicalForm = lexicalForm; //If not Initialised then required by asLiteral() etc.
this.sourceLexicalForm = lexicalForm;
}

/**
Expand Down Expand Up @@ -190,6 +213,8 @@ public GeometryWrapper(GeometryWrapper geometryWrapper) {
this.srsInfo = geometryWrapper.srsInfo;
this.dimensionInfo = geometryWrapper.dimensionInfo;
this.lexicalForm = geometryWrapper.lexicalForm;
this.sourceLexicalForm = geometryWrapper.sourceLexicalForm;
this.geometryTypeURI = geometryWrapper.geometryTypeURI;
}

/**
Expand Down Expand Up @@ -387,6 +412,54 @@ public String getGeometryType() {
return parsingGeometry.getGeometryType();
}

/**
* Returns the geometry subtype URI defined by this wrapper's datatype,
* including for typed empty geometries. The URI is supplied at construction
* or resolved by the datatype and cached on the first successful lookup.
* Specialized subtypes are not inferred from the shape of the coordinates.
*
* @return The subtype URI as a string.
*
* @throws DatatypeFormatException if the datatype cannot resolve the geometry type.
*/
public String getGeometryTypeURI() {
if (geometryTypeURI == null) {
geometryTypeURI = getGeometryDatatype().getGeometryTypeURI(this);
}
return geometryTypeURI;
}

/**
* Returns whether the coordinate layout includes Z, including for empty geometries.
* Uses the wrapper's dimension metadata without aggregating member layouts.
* A WKT collection without a Z/M marker has XY metadata even if members
* declare their own Z/M layouts.
*/
public boolean is3D() {
CoordinateSequenceDimensions dimensions = getCoordinateSequenceDimensions();
return dimensions == CoordinateSequenceDimensions.XYZ || dimensions == CoordinateSequenceDimensions.XYZM;
}

/**
* Returns whether the coordinate layout includes M, including for empty geometries.
* Uses the wrapper's dimension metadata without aggregating member layouts.
* A WKT collection without a Z/M marker has XY metadata even if members
* declare their own Z/M layouts.
*/
public boolean isMeasured() {
CoordinateSequenceDimensions dimensions = getCoordinateSequenceDimensions();
return dimensions == CoordinateSequenceDimensions.XYM || dimensions == CoordinateSequenceDimensions.XYZM;
}

/**
* Returns the number of direct members of a Multi-geometry or GeometryCollection.
* Atomic geometries count as one, including empty atomic geometries; nested
* collections are not flattened. A collection with no members counts as zero.
*/
public int getNumGeometries() {
return parsingGeometry.getNumGeometries();
}

/**
*
* @return GeometryDatatype of the literal.
Expand Down Expand Up @@ -1023,6 +1096,19 @@ public DimensionInfo getDimensionInfo() {
return dimensionInfo;
}

/**
* Returns the serialized geometry text supplied to the constructor, such as
* WKT or GML. Unlike {@link #getLexicalForm()}, this method does not generate
* text when none was supplied. Calling serialization methods does not change
* the returned value.
*
* @return The supplied text, or null if none was supplied. An empty string
* means that an empty literal was supplied; it is distinct from null.
*/
public String getSourceLexicalForm() {
return sourceLexicalForm;
}

/**
*
* @return String literal of Geometry Wrapper.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
import org.apache.jena.datatypes.DatatypeFormatException;
import org.apache.jena.geosparql.implementation.DimensionInfo;
import org.apache.jena.geosparql.implementation.GeometryWrapper;
import org.apache.jena.geosparql.implementation.parsers.gml.GMLGeometryTypes;
import org.apache.jena.geosparql.implementation.parsers.gml.GMLReader;
import org.apache.jena.geosparql.implementation.parsers.gml.GMLWriter;
import org.apache.jena.geosparql.implementation.vocabulary.Geo;
import org.apache.jena.geosparql.implementation.vocabulary.GeoSPARQL_URI;
import org.jdom2.JDOMException;
import org.locationtech.jts.geom.Geometry;

Expand Down Expand Up @@ -85,12 +87,28 @@ public GeometryWrapper read(String geometryLiteral) {
String srsURI = gmlReader.getSrsURI();
DimensionInfo dimensionInfo = gmlReader.getDimensionInfo();

return new GeometryWrapper(geometry, srsURI, URI, dimensionInfo, geometryLiteral);
String geometryTypeURI = GeoSPARQL_URI.GML_URI + gmlReader.getGmlGeometryType();
return new GeometryWrapper(geometry, srsURI, URI, dimensionInfo, geometryLiteral, geometryTypeURI);
} catch (JDOMException | IOException ex) {
throw new DatatypeFormatException("Illegal GML literal:" + geometryLiteral + ". " + ex.getMessage());
}
}

@Override
public String getGeometryTypeURI(GeometryWrapper geometry) {
String source = geometry.getSourceLexicalForm();
if (source != null) {
// Constructors accepting source text without a type URI use this fallback.
// read() prepopulates the wrapper's type, so normal lookups skip this path.
try {
return GeoSPARQL_URI.GML_URI + GMLReader.readGeometryType(source);
} catch (JDOMException | IOException ex) {
throw new DatatypeFormatException("Unable to read GML geometry type", ex);
}
}
return GeoSPARQL_URI.GML_URI + GMLGeometryTypes.fromJts(geometry.getParsingGeometry());
}

@Override
public String toString() {
return "GMLDatatype{" + URI + '}';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ public GeometryDatatype(String uri) {

public abstract GeometryWrapper read(String geometryLiteral);

/**
* Resolves a geometry subtype URI using this datatype's type system.
* Datatypes supporting type resolution override this operation to define
* their type mapping. The default implementation throws a datatype error.
*
* @param geometry A wrapper whose geometry datatype is this datatype.
* @return The subtype URI as a non-null string.
* @throws DatatypeFormatException if type resolution is unsupported for this datatype or geometry.
*/
public String getGeometryTypeURI(GeometryWrapper geometry) {
throw new DatatypeFormatException("Geometry type resolution is not supported for datatype: " + getURI());
}

/**
* This method Parses the Geometry Literal to the JTS Geometry
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.jena.geosparql.implementation.parsers.wkt.WKTReader;
import org.apache.jena.geosparql.implementation.parsers.wkt.WKTWriter;
import org.apache.jena.geosparql.implementation.vocabulary.Geo;
import org.apache.jena.geosparql.implementation.vocabulary.GeoSPARQL_URI;
import org.locationtech.jts.geom.Geometry;

/**
Expand Down Expand Up @@ -93,6 +94,16 @@ public GeometryWrapper read(String geometryLiteral) {
return new GeometryWrapper(geometry, srsURI, URI, dimensionInfo, geometryLiteral);
}

@Override
public String getGeometryTypeURI(GeometryWrapper geometry) {
String type = geometry.getGeometryType();
return switch (type) {
case "Point", "LineString", "LinearRing", "Polygon", "MultiPoint", "MultiLineString",
"MultiPolygon", "GeometryCollection" -> GeoSPARQL_URI.SF_URI + type;
default -> throw new DatatypeFormatException("Unsupported Simple Features geometry type: " + type);
};
}

@Override
public String toString() {
return "WKTDatatype{" + URI + '}';
Expand Down
Loading