Overriding Server Logic

There are a few places where the server logic can be extended:

There are a few places that allow to customize the WFS logic:

View Layer

The following methods of the WFSView can be overwritten:

  • get_feature_types() to dynamically generate all exposed features.
  • get_service_description() to dynamically generate the description.
  • dispatch() to implement basic auth.

Feature Layer

Overriding FeatureType allows to change how particular features and fields are exposed. It can also override the internal XML Schema Definition (XSD) that all output and query filters read.

This can also adjust the

  • Overriding check_permissions() allows to perform a permission check before the feature can be read (e.g. a login role check).
  • Overriding get_queryset() allows to define the queryset per request.
  • Overriding xsd_type constructs the internal XSD definition of this feature.
  • Overriding xsd_type_class defines which class constructs the XSD.

The field() function returns a FeatureField. Instances of this class can be passed directly to the FeatureType(fields=...) parameter, and override these attributes:

  • xsd_element constructs the internal XSD that filters and output formats use.
  • xsd_element_class defines which class defines the attribute.

XSD Layer

The feature fields generate an internal XML Schema Definition (XSD) that defines how properties are read, and where the underlying ORM field/relation can be found. These types can be overwritten for custom behavior, and then be returned by custom FeatureType and FeatureField objects.

  • XsdComplexType defines a complete class with elements and attributes.
  • XsdElement defines a property that becomes a normal element.
  • XsdAttribute defines the attributes (only gml:id is currently rendered).

The elements and attributes have the following fields:

  • orm_path - returns where to find the ORM relation.
  • orm_field - returns the first part of the ORM relation.
  • orm_relation - returns the ORM relation as path and final field name.
  • get_value() - how to read the attribute value.
  • format_value() - format raw-retrieved values from the database (e.g .values() query).
  • to_python() - how to cast input data.
  • validate_comparison() - checks a field supports a certain data type.
  • build_lhs_part() - how to generate the ORM left-hand-side.
  • build_rhs_part() - how to generate the ORM right-hand-side.

Custom Filter Functions

Warning

While the machinery to hook new functions is in place, this part is still in development.

As part of the WFS Filter Encoding, a client can execute a function against a server. These are executed with ?REQUEST=GetFeature&FILTER...

An expression such as: table_count == Add(“previous_table_count”, 100) would be encoded in the following way using the Filter Encoding Specification (FES):

<fes:Filter xmlns:fes="http://www.opengis.net/fes/2.0">
    <fes:PropertyIsEqualTo>
        <fes:ValueReference>table_count</fes:ValueReference>
        <fes:Function name="Add">
            <fes:ValueReference>previous_table_count</fes:ValueReference>
            <fes:Literal>100</fes:Literal>
        </fes:Function>
    </fes:PropertyIsEqualTo>
</fes:Filter>

These FES functions can be defined in the project, by generating a corresponding database function.

Use gisserver.parsers.fes_function_registry to register new functions:

from django.db.models import functions
from gisserver.parsers import fes_function_registry
from gisserver.types import XsdTypes


# Either link an exising Django ORM function:

function_registry.register(
    "atan",
    functions.ATan,
    arguments={"value": XsdTypes.double},
    returns=XsdTypes.double,
)


# Or link a parsing logic that generates an ORM function/object:

@function_registry.register(
    name="Add",
    arguments=dict(value1=XsdTypes.double, value2=XsdTypes.double),
    returns=XsdTypes.double,
)
def fes_add(value1, value2):
    return F(value1) + value2

Each FES function should return a Django ORM Func or Combinable object.

Custom Stored Procedures

Warning

While the machinery to add new stored procedures is in place, this part is still in development.

Aside from filters, a WFS server can also expose “stored procedures”. These are executed with ?REQUEST=GetFeature&STOREDQUERY_ID=... By default, only GetFeatureById is built-in.

These stored procedures can be defined like this:

from gisserver.queries import StoredQuery, stored_query_registry
from gisserver.types import XsdTypes


@stored_query_registry.register(
    id="GetRecentChanges",
    title="Get all recent changed features",
    abstract="All recent changes...",
    parameters={"date": XsdTypes.date},
)
class GetRecentChanges(StoredQuery):
    ...

For a simple implementation, the following methods need to be overwritten:

  • get_type_names() defines which feature types this query applies to.
  • compile_query() defines how to filter the queryset.

For full control, these methods can also be overwritten instead:

  • get_queryset() to define the full results.
  • get_hits() to return the collection for RESULTTYPE=hits.
  • get_results() to return the collection for RESULTTYPE=results.