gisserver.types module

Internal XSD type definitions.

These types are the internal schema definition, and the foundation for all output generation.

The end-users of this library typically create a WFS feature type definition by using the FeatureType / FeatureField classes.

The feature type classes use the model metadata to construct the internal XMLSchema structure. Nearly all WFS requests are handled by walking this structure (like DescribeFeatureType or GetFeature). The rendered output is created by walking through this structure, and writing the XML elements. All search queries (using XPath) are processed by resolving the corresponding element/attributes, to find to the underlying Django model field.

The structure has the following elements:

  • XsdElement to define the schema of a single XML element (<xsd:element>).

  • XsdAttribute to define the schema of a single XML attribute (<xsd:attribute>).

Each XMLSchema node defines it’s “data type” as either:

  • XsdTypes for simple well-known data types (e.g. text/int, etc..)

  • XsdComplexType for a complete class definition (holding elements and attributes)

Custom field types could also generate these field types.

XML Schema Types

class gisserver.types.XsdAnyType

Base class for all types used in the XML definition. This includes the enum values (XsdTypes) for well-known types, adn the XsdComplexType that represents a while class definition.

is_complex_type = False

Whether this is a complex type

is_geometry = False

Whether this is a geometry

name: str

Local name of the XML element

namespace = None

Namespace of the XML element

to_python(raw_value)

Convert a raw string value to this type representation

class gisserver.types.XsdTypes(*values)

Bases: XsdAnyType, Enum

Brief enumeration of common XMLSchema types.

The XsdElement and XsdAttribute can use these enum members to indicate their value is a well-known XML Schema. Some GML types are included as well.

Each member value is a fully qualified XML name. The output rendering will convert these to the chosen prefixes.

__init__(value)
anyType = '{http://www.w3.org/2001/XMLSchema}anyType'
anyURI = '{http://www.w3.org/2001/XMLSchema}anyURI'
base64Binary = '{http://www.w3.org/2001/XMLSchema}base64Binary'
boolean = '{http://www.w3.org/2001/XMLSchema}boolean'
byte = '{http://www.w3.org/2001/XMLSchema}byte'
date = '{http://www.w3.org/2001/XMLSchema}date'
dateTime = '{http://www.w3.org/2001/XMLSchema}dateTime'
decimal = '{http://www.w3.org/2001/XMLSchema}decimal'
double = '{http://www.w3.org/2001/XMLSchema}double'
duration = '{http://www.w3.org/2001/XMLSchema}duration'
float = '{http://www.w3.org/2001/XMLSchema}float'
gYear = '{http://www.w3.org/2001/XMLSchema}gYear'
gmlAbstractFeatureType = '{http://www.opengis.net/gml/3.2}AbstractFeatureType'

A feature that has a gml:name and gml:boundedBy as possible child element.

gmlAbstractGMLType = '{http://www.opengis.net/gml/3.2}AbstractGMLType'

The base of gml:AbstractFeatureType

gmlAbstractGeometryType = '{http://www.opengis.net/gml/3.2}AbstractGeometryType'

A direct geometry value, sometimes used as function argument type.

gmlBoundingShapeType = '{http://www.opengis.net/gml/3.2}BoundingShapeType'

The type for <gml:boundedBy> elements.

gmlCodeType = '{http://www.opengis.net/gml/3.2}CodeType'

The type for <gml:name> elements.

gmlCurvePropertyType = '{http://www.opengis.net/gml/3.2}CurvePropertyType'
gmlEnvelopeType = '{http://www.opengis.net/gml/3.2}EnvelopeType'

The type for <gml:Envelope> elements, sometimes used as function argument type.

gmlGeometryPropertyType = '{http://www.opengis.net/gml/3.2}GeometryPropertyType'
gmlMultiCurvePropertyType = '{http://www.opengis.net/gml/3.2}MultiCurvePropertyType'
gmlMultiGeometryPropertyType = '{http://www.opengis.net/gml/3.2}MultiGeometryPropertyType'
gmlMultiPointPropertyType = '{http://www.opengis.net/gml/3.2}MultiPointPropertyType'
gmlMultiSurfacePropertyType = '{http://www.opengis.net/gml/3.2}MultiSurfacePropertyType'
gmlPointPropertyType = '{http://www.opengis.net/gml/3.2}PointPropertyType'
gmlSurfacePropertyType = '{http://www.opengis.net/gml/3.2}SurfacePropertyType'
hexBinary = '{http://www.w3.org/2001/XMLSchema}hexBinary'
int = '{http://www.w3.org/2001/XMLSchema}int'
integer = '{http://www.w3.org/2001/XMLSchema}integer'
language = '{http://www.w3.org/2001/XMLSchema}language'
long = '{http://www.w3.org/2001/XMLSchema}long'
property name: str

Overwrites enum.name to return the XML local name. This is used for to_qname().

nonNegativeInteger = '{http://www.w3.org/2001/XMLSchema}nonNegativeInteger'
short = '{http://www.w3.org/2001/XMLSchema}short'
string = '{http://www.w3.org/2001/XMLSchema}string'
time = '{http://www.w3.org/2001/XMLSchema}time'
to_python(raw_value)

Convert a raw string value to this type representation.

Raises:

ExternalParsingError – When the value can’t be converted to the proper type.

token = '{http://www.w3.org/2001/XMLSchema}token'
unsignedByte = '{http://www.w3.org/2001/XMLSchema}unsignedByte'
unsignedInt = '{http://www.w3.org/2001/XMLSchema}unsignedInt'
unsignedLong = '{http://www.w3.org/2001/XMLSchema}unsignedLong'
unsignedShort = '{http://www.w3.org/2001/XMLSchema}unsignedShort'
class gisserver.types.XsdComplexType(name: str, namespace: str | None = None, elements: list[XsdElement] = <factory>, attributes: list[XsdAttribute] = <factory>, base: XsdAnyType | None = None, source: type[Model] | None = None)

Bases: XsdAnyType

Define an <xsd:complexType> that represents a whole class definition.

Typically, this maps into a Django model, with each element pointing to a model field. For example:

XsdComplexType(
    "PersonType",
    elements=[
        XsdElement("name", type=XsdTypes.string),
        XsdElement("age", type=XsdTypes.integer),
        XsdElement("address", type=XsdComplexType(
            "AddressType",
            elements=[
                XsdElement("street", type=XsdTypes.string),
                ...
            ]
        )),
    ],
    attributes=[
        XsdAttribute("id", type=XsdTypes.integer),
    ],
)

A complex type can hold multiple XsdElement and XsdAttribute nodes as children, composing an object. Its base may point to a XsdComplexType as base class, allowing to define those inherited elements too.

Each element can be a complex type themselves, to create a nested class structure. That also allows embedding models with their relations into a single response.

Note

Good to know This object definition is the internal “source of truth” regarding which field names and field elements are used in the WFS server:

  • The DescribeFeatureType request uses this definition to render the matching XMLSchema.

  • Incoming XPath queries are parsed using this object to resolve the XPath to model attributes.

Objects of this type are typically generated by the FeatureType and ComplexFeatureField classes, using the Django model data.

By default, The base type is detected as <gml:AbstractFeatureType>, when there is a geometry element in the definition.

__init__(name: str, namespace: str | None = None, elements: list[XsdElement] = <factory>, attributes: list[XsdAttribute] = <factory>, base: XsdAnyType | None = None, source: type[Model] | None = None) None
property all_complex_elements: list[_XsdElement_WithComplexType]

Shortcut to get all elements with children. This mainly exists to provide a structure to mimic what’s used when PROPERTYNAME is part of the request.

attributes: list[XsdAttribute]

All attributes in this class

base: XsdAnyType | None = None

The base class of this type. Typically gml:AbstractFeatureType, which provides the <gml:name> and <gml:boundedBy> elements.

property complex_elements: list[_XsdElement_WithComplexType]

Shortcut to get all elements with a complex type. To get all complex elements recursively, read all_complex_elements.

elements: list[XsdElement]

All local elements in this class

property elements_including_base: list[XsdElement]

The local and inherited elements of this XSD type.

property flattened_elements: list[XsdElement]

Shortcut to get all elements with a flattened model attribute

property geometry_elements: list[GeometryXsdElement]

Shortcut to get all geometry elements

property is_complex_type: bool

Always indicates this is a complex type.

name: str

Internal class name (without XML namespace/prefix)

resolve_element_path(xpath: str, ns_aliases: dict[str, str]) list[XsdNode] | None

Resolve a xpath reference to the actual node. This returns the whole path, including in-between relations, if a match was found.

This is used by resolve_element() to convert a request XPath element into the ORM attributes for database queries.

source: type[Model] | None = None

The Django model class that this type was based on.

property xml_name

Name in the XMLSchema (e.g. {http://example.org/namespace}:SomeClass).

XML Schema Elements

class gisserver.types.XsdNode(name: str, type: XsdAnyType, namespace: xmlns | str | None, *, source: models.Field | models.ForeignObjectRel | None = None, model_attribute: str | None = None, absolute_model_attribute: str | None = None, feature_type: FeatureType | None = None)

Base class for XsdElement and XsdAttribute.

This contains all common mapping/resolving that both elements and attributes share. For instance, how XML nodes are mapped into ORM paths, converted into ORM filters, parse query input and read model attributes to write as output.

__init__(name: str, type: XsdAnyType, namespace: xmlns | str | None, *, source: models.Field | models.ForeignObjectRel | None = None, model_attribute: str | None = None, absolute_model_attribute: str | None = None, feature_type: FeatureType | None = None)
Parameters:
  • name – The local name of the element.

  • type – The XML Schema type of the element, can also be a XsdComplexType.

  • namespace – XML namespace URI.

  • source – Original Model field, which can provide more metadata/parsing.

  • model_attribute – The Django model path that this element accesses.

  • absolute_model_attribute – The full path, including parent elements.

  • feature_type – Typically assigned in bind(), needed by some get_value() functions.

build_lhs_part(compiler: CompiledQuery, match: ORMPath)

Give the ORM part when this element is used as left-hand-side of a comparison. This is needed for queries like “<element> == <value>”

build_rhs_part(compiler: CompiledQuery, match: ORMPath)

Give the ORM part when this element would be used as right-hand-side. This is needed for queries like “<value> == <element>” or “<element> == <element>”.

feature_type: FeatureType | None

A link back to the parent that described the feature this node is a part of. This helps to perform additional filtering in side meth:get_value: based on user policies.

format_raw_value(value)

Allow to apply some final transformations on a value. This is mainly used to support @gml:id which includes a prefix.

get_value(instance: Model)

Provide the value for the data

property is_array: bool

Tell whether this node is backed by an PostgreSQL Array Field.

is_attribute = False

Whether this node is an XsdAttribute (avoids slow isinstance() checks)

property is_flattened: bool

Whether the field is a lookup to a relation.

is_many = False

Whether this node can occur multiple times.

property local_orm_path: str

The ORM field lookup to perform.

model_attribute: str | None

Which field to read from the model to get the value This supports dot notation to access related attributes.

name: str

The local name of the XML element

namespace: xmlns | str | None

XML Namespace of the element

property orm_field: str

The direct ORM field that provides this property; the first relative level. Typically, this is the same as the field name.

property orm_path: str

The ORM field lookup to perform.

property orm_relation: tuple[str | None, str]

The ORM field and parent relation. Note this isn’t something like “self.parent.orm_path”, as this mode may have a dotted-path to its source attribute.

relative_orm_path(parent: XsdElement | None = None) str

The ORM field lookup to perform, relative to the parent element.

source: models.Field | models.ForeignObjectRel | None

Which field to read from the model to get the value This supports dot notation to access related attributes.

to_python(raw_value: str)

Convert a raw value to the Python data type for this element type. :raises ValidationError: When the value isn’t allowed for the field type.

type: XsdAnyType

The data type of the element/attribute, both XsdComplexType and XsdTypes are allowed.

validate_comparison(raw_value: str, lookup, tag=None)

Validate whether the input value can be used in a comparison. This avoids comparing a database DATETIME object to an integer.

The raw string value can be passed here. Auto-cased values could raise an TypeError due to being unsupported by the validation.

Parameters:
  • raw_value – The string value taken from the XML node.

  • lookup – The ORM lookup (e.g. equals or fes_like).

  • tag – The filter operator tag name, e.g. PropertyIsEqualTo.

Returns:

The parsed Python value.

property xml_name

The XML element/attribute name.

class gisserver.types.XsdElement(name: str, type: XsdAnyType, namespace: xmlns | str | None, *, nillable: bool | None = None, min_occurs: int | None = None, max_occurs: int | Literal['unbounded'] | None = None, source: models.Field | models.ForeignObjectRel | None = None, model_attribute: str | None = None, absolute_model_attribute: str | None = None, feature_type: FeatureType | None = None)

Bases: XsdNode

Declare an XSD element.

Typically, this maps into a Django model field.

This holds the definition for a single property in the WFS server. It’s used in DescribeFeatureType to output the field metadata, and used in GetFeature to access the actual value from the object. Overriding XsdNode.get_value() allows to override this logic.

The name may differ from the underlying XsdNode.model_attribute, so the WFS server can use other field names then the underlying model.

A dotted-path notation can be used for XsdNode.model_attribute to access a related field. For the WFS client, the data appears to be flattened.

__init__(name: str, type: XsdAnyType, namespace: xmlns | str | None, *, nillable: bool | None = None, min_occurs: int | None = None, max_occurs: int | Literal['unbounded'] | None = None, source: models.Field | models.ForeignObjectRel | None = None, model_attribute: str | None = None, absolute_model_attribute: str | None = None, feature_type: FeatureType | None = None)
Parameters:
  • name – The local name of the element.

  • type – The XML Schema type of the element, can also be a XsdComplexType.

  • namespace – XML namespace URI.

  • source – Original Model field, which can provide more metadata/parsing.

  • model_attribute – The Django model path that this element accesses.

  • absolute_model_attribute – The full path, including parent elements.

  • feature_type – Typically assigned in bind(), needed by some get_value() functions.

property is_many: bool

Tell whether the XML element can be rendered multiple times. Note this happens both with array fields and “…_to_many” relations.

max_occurs: int | Literal['unbounded'] | None

The maximum number of times this element occurs in the output.

min_occurs: int | None

The minimal number of times the element occurs in the output.

nillable: bool | None

Whether the element can be null

class gisserver.types.XsdAttribute(name: str, type: XsdAnyType = XsdTypes.string, *, namespace: xmlns | str | None = None, use: str = 'optional', source: models.Field | models.ForeignObjectRel | None = None, model_attribute: str | None = None, absolute_model_attribute: str | None = None, feature_type: FeatureType | None = None)

Bases: XsdNode

Declare an XSD attribute.

Typically, this maps into a Django model field.

Most fields are mapped into XML Elements (XsdElement). However, WFS also supports XML attributes, and queries against them. This class is uses to support filters against attributes like “gml:id”.

__init__(name: str, type: XsdAnyType = XsdTypes.string, *, namespace: xmlns | str | None = None, use: str = 'optional', source: models.Field | models.ForeignObjectRel | None = None, model_attribute: str | None = None, absolute_model_attribute: str | None = None, feature_type: FeatureType | None = None)
Parameters:
  • name – The local name of the element.

  • type – The XML Schema type of the element, can also be a XsdComplexType.

  • namespace – XML namespace URI.

  • source – Original Model field, which can provide more metadata/parsing.

  • model_attribute – The Django model path that this element accesses.

  • absolute_model_attribute – The full path, including parent elements.

  • feature_type – Typically assigned in bind(), needed by some get_value() functions.

is_attribute = True

Whether this node is an XsdAttribute (avoids slow isinstance() checks)

type: XsdTypes

The data type of the element/attribute, both XsdComplexType and XsdTypes are allowed.

use: str = 'optional'

Specialized Schema Elements

class gisserver.types.GeometryXsdElement(name: str, type: XsdAnyType, namespace: xmlns | str | None, *, nillable: bool | None = None, min_occurs: int | None = None, max_occurs: int | Literal['unbounded'] | None = None, source: models.Field | models.ForeignObjectRel | None = None, model_attribute: str | None = None, absolute_model_attribute: str | None = None, feature_type: FeatureType | None = None)

Bases: XsdElement

A subtype for the XsdElement that provides access to geometry data.

This declares an element such as:

<app:geometry>
    <gml:Point>...</gml:Point>
</app:geometry>

Hence, the namespace of this element isn’t the GML namespace, only the type it points to is geometry data.

The source is guaranteed to point to a GeometryField, and can be a GeneratedField in Django 5 as long as its output_field points to a GeometryField.

source: GeometryField | models.GeneratedField

Which field to read from the model to get the value This supports dot notation to access related attributes.

property source_srid: int

Tell which Spatial Reference Identifier the source information is stored under.

class gisserver.types.GmlIdAttribute(type_name: str, source: models.Field | models.ForeignObjectRel | None = None, model_attribute='pk', absolute_model_attribute=None, feature_type: FeatureType | None = None)

Bases: XsdAttribute

A virtual gml:id="..." attribute that can be queried. This subclass has overwritten get_value() logic to format the value.

__init__(type_name: str, source: models.Field | models.ForeignObjectRel | None = None, model_attribute='pk', absolute_model_attribute=None, feature_type: FeatureType | None = None)
Parameters:
  • name – The local name of the element.

  • type – The XML Schema type of the element, can also be a XsdComplexType.

  • namespace – XML namespace URI.

  • source – Original Model field, which can provide more metadata/parsing.

  • model_attribute – The Django model path that this element accesses.

  • absolute_model_attribute – The full path, including parent elements.

  • feature_type – Typically assigned in bind(), needed by some get_value() functions.

format_raw_value(value)

Format the value as retrieved from the database.

get_value(instance: Model)

Render the value.

type_name: str
class gisserver.types.GmlNameElement(model_attribute: str, source: Field | ForeignObjectRel | None = None, feature_type=None)

Bases: XsdElement

A subclass to handle the <gml:name> element. This displays a human-readable title for the object.

Currently, this just reads a single attribute, but it can be extended to support formatted names (although that would make comparisons on element@gml:name more complex).

__init__(model_attribute: str, source: Field | ForeignObjectRel | None = None, feature_type=None)
Parameters:
  • name – The local name of the element.

  • type – The XML Schema type of the element, can also be a XsdComplexType.

  • namespace – XML namespace URI.

  • source – Original Model field, which can provide more metadata/parsing.

  • model_attribute – The Django model path that this element accesses.

  • absolute_model_attribute – The full path, including parent elements.

  • feature_type – Typically assigned in bind(), needed by some get_value() functions.

get_value(instance: Model)

Override value retrieval to retrieve the value from the feature type.

class gisserver.types.GmlBoundedByElement(feature_type)

Bases: XsdElement

A subclass to handle the <gml:boundedBy> element.

This override makes sure this non-model element data can be included in the XML tree like every other element. Its value is the complete bounding box of the feature type data.

__init__(feature_type)
Parameters:
  • name – The local name of the element.

  • type – The XML Schema type of the element, can also be a XsdComplexType.

  • namespace – XML namespace URI.

  • source – Original Model field, which can provide more metadata/parsing.

  • model_attribute – The Django model path that this element accesses.

  • absolute_model_attribute – The full path, including parent elements.

  • feature_type – Typically assigned in bind(), needed by some get_value() functions.

build_lhs_part(compiler: CompiledQuery, match: ORMPath)

Give the ORM part when this element is used as left-hand-side of a comparison.

build_rhs_part(compiler: CompiledQuery, match: ORMPath)

Give the ORM part when this element would be used as right-hand-side

get_value(instance: Model, crs: CRS | None = None) BoundingBox | None

Provide the value of the <gml:boundedBy> field, which is the bounding box for a single instance.

This is only used for native Python rendering. When the database rendering is enabled (GISSERVER_USE_DB_RENDERING=True), the calculation is entirely performed within the query.

XPath Resolving

class gisserver.types.ORMPath(orm_path: str, orm_filters: Q | None = None, is_many=False)

Base class to provide raw XPath results.

This base class is designed to allow other query types (besides XPath) too, and allows inserting raw data directly to the query compiler (for unit testing).

__init__(orm_path: str, orm_filters: Q | None = None, is_many=False)

Base constructor just assigns items. Overwritten classes likely replace this with properties.

build_lhs(compiler: CompiledQuery)

Give the ORM part when this element is used as left-hand-side of a comparison. For example: path == value.

build_rhs(compiler: CompiledQuery)

Give the ORM part when this element would be used as right-hand-side. For example: path1 == path2 or value == path.

class gisserver.types.XPathMatch(feature_type: FeatureType, nodes: list[XsdNode], query: str)

Bases: ORMPath

The ORM path result from am XPath query.

This result object defines how to resolve an XPath to an ORM object.

__init__(feature_type: FeatureType, nodes: list[XsdNode], query: str)

Base constructor just assigns items. Overwritten classes likely replace this with properties.

build_lhs(compiler: CompiledQuery)

Give the ORM part when this element is used as left-hand-side of a comparison. For example: path == value.

build_rhs(compiler: CompiledQuery)

Give the ORM part when this element would be used as right-hand-side. For example: path1 == path2 or value == path.

property child: XsdNode

Return only the final element

property is_many: bool

Return whether this ORM path walks over an element that occurs multiple times

nodes: list[XsdNode]

The matched element, with all it’s parents.

orm_filters: Q | None

The additional filters are needed (due to [@attr=..] syntax).

property orm_path: str

Give the Django ORM path (field__relation__relation2) to the result.

query: str

The source XPath query