Geometry processing functions

centroid

Arguments: [Geom]

Returns: Point

Implements centroid geometric function as per https://docs.geoserver.org/latest/en/user/filter/function_reference.html#geometric-functions

intersection

Arguments: [Geom, Geom]

Returns: Geom

intersection

bounds

Arguments: [relation: Relation[{}]]

Returns: Geom

Returns a polygon containing the spatial bounds (extent) of the given relation or coverage. The returned bounds may be used with other geometry functions, e.g intersects(exposure, bounds(bookmark('my-hazard'))) would return true for each exposure that has some intersection with the my-hazard layer’s spatial extent.

The spatial bounds is a rectangular box that is the smallest size to cover all of the geometries. For further information see https://en.wikipedia.org/wiki/Minimum_bounding_box

buffer

Arguments: [geom: Geom, distance: Floating, options: {cap=>WithinSet(type=Text, allowed=[square, round, flat]), vertex=>WithinSet(type=Text, allowed=[round, mitre, bevel])}]

Returns: Geom

Buffer (or enlarge) a geometry by a given distance in metres. By default, buffering has the effect of rounding off corners and line endings in the original geometry, but this can be customized with the optional options argument.

options.vertex' affects how external corners are enlarged. Given a 90 degree corner, like on a square,round(the default) will round it off,mitrewill preserve the same sharp corners as the original,bevel` will cut a 45 degree line across the corner.

options.cap affects how the end of a line is capped off. round (the default) will round it off, square will push the ends of the line outward but the end will be square, flat is similar to square but will not make the line longer.

Note that options.vertex will not have any effect on line endings - only options.cap will. Also note that using a flat cap when buffering a point will result in an empty polygon.

create_point

Arguments: [Floating, Floating]

Returns: Point

Creates a geometry point using the given x, y coordinate

geom_from_wkt

Arguments: [wkt_text: Text]

Returns: Geom

Create a geometry object from a Well-Known Text (WKT) formatted geometry string, e.g. geom_from_wkt('POINT (1 2)'). See https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry for more information about the well-known text format.

reproject

Arguments: [geom: Geom, crs: Text]

Returns: Geom

Reproject a geometry into another coordinate reference system (CRS). E.g to reproject to ‘EPSG:2193’ use reproject(geom, 'EPSG:2193').

Alternately the crs argument may be another geometry. In this case the input geometry will be reprojected to the CRS that the other geometry is in. E.g reproject(geom, other_geom)

measure_area

Arguments: [geom: Geom]

Returns: Floating

Measure the area of geometry in metres squared (m²). Note that geometries that are specified in degrees are reprojected before being measured. This will lose accuracy for geometries that are larger than 6° in either direction and eventually fail if too big.

measure_length

Arguments: [geom: Geom]

Returns: Floating

Measure the length of a geometry’s perimeter in metres (m). Note that geometries that are specified in degrees are reprojected before being measured. This will lose accuracy for geometries that are larger than 6° in either direction and eventually fail if too big.

measure

Arguments: [geom: Geom]

Returns: Floating

Will measure either the length or area of a geometry, depending on its geometry type. Points will always return 0. Lines will return the length as per measure_length. Polygon‘s will return the area as per measure_area. GeometryCollections will return the sum of all geometries in the collection

to_coverage

Arguments: [values: Relation[{}], options: Anything]

Returns: Coverage[Anything]

Produces a coverage from a collection of tuples. The values argument is the data to turn into a coverage, and can be either a relation or a list of tuples. This function can also be used as an aggregate function to collect tuples (i.e. in a ‘group’ pipeline step).

The produced coverage can be sampled using one of the sampling functions (e.g. sample, sample_centroid or sample_one). The coverage is constructed by adding all of the tuples into a spatial index, which is queried for matching tuples when sampled. The tuples in the coverage must contain a single geometry member that is spatially referenced.

There are two types of index that can be constructed, ‘intersection’ and ‘nearest_neighbour’, which can be specified by the index option, e.g. to_coverage(values, options: {index: 'intersection'}). The default index is ‘intersection’.

The intersection index is most appropriate for indexing polygonal features. The sampling operation will query the index for any intersecting features, and return those that match according to the semantics of the sampling operation that was used. Setting the option intersection_cut to true can improve performance considerably when you intend to sample the index using either sample_one or sample_centroid. This has the most dramatic effect when the indexed features are large or irregular, at the cost of higher memory usage. For more advanced tuning options, refer to the IntersectionIndex‘s source code.

The nearest_neighbour index is best when indexing point features. A sampling operation will query the index for the nearest feature’s point (with a max distance cut off). Only sample_centroid is currently supported for this type of index. The cutoff distance must be supplied as the nearest_neighbour_max_distance option. E.g to use a nearest neighbour index with a cutoff of one kilometre use to_coverage(tuples, options:  {index: 'nearest_neighbour', nearest_neighbour_max_distance: 1000})

The simplest example of using this function in a pipeline is with bookmark data (typically a relation), e.g. sample_centroid(building.geom, to_coverage(bookmark(‘hazard’))) Note that the above sampling operation will work regardless of whether the ‘hazard’ bookmark is vector data (i.e. a relation) or raster data (i.e. already a coverage).

sample

Arguments: [geometry: Geom, coverage: Coverage[Anything]]

Returns: List[{geometry=>Geom, sampled=>Anything}]

Selects all the values from a coverage that intersect with the given geometry.

This function is useful for determining details such as the total amount that a polygon or line-string is exposed to a hazard, or the specific min/mean/max/etc hazard intensity measure for a given feature.

The given geometry may intersect the coverage multiple times (e.g. multiple pixels in a GeoTIFF), and so a list of intersections is returned. Each list item contains a geometry attribute, which is an intersecting piece of the input geometry, and a sampled attribute, which is the value returned from the coverage at that specific location.

Note that when building a simple model, the sample_one function might be easier to use instead, as that lets you work with a single sampled value instead of a list of values.

sample_centroid

Arguments: [geometry: Geom, coverage: Coverage[Anything]]

Returns: Anything

Select a single value from a coverage by sampling at the geometry’s centroid (centre point). This function will return null if there is no sample at the centroid even if there are samples that intersect other parts of the geometry.

sample_one

Arguments: [geometry: Geom, coverage: Coverage[Anything], buffer-distance: Floating]

Returns: Anything

Selects a single value from a coverage. If the geometry intersects with multiple values in the coverage then the value closest to the geometry centroid is chosen. If nothing is found and ‘buffer-distance’ is specified (in metres) then the geometry is buffered (enlarged) by that amount and the coverage is sampled again, but this time the value closest to the geometry is chosen.

Note: buffering is done with an estimated metric-to-map-unit distance, which can be inaccurate for degree (lat/long) based projections.

segment

Arguments: [geometry: Geom, distance: Floating]

Returns: List[Geom]

Cut geometries up by a distance specified in metres (m). Linestrings are cut into pieces of at most distance metres in length. Polygons are cut using a grid - each grid cell is a square, where each side is distance metres in length. E.g. cutting a polygon by 100m will result in grid cells that are 100m x 100m (10000m²) squares. The grid is aligned to each polygon’s centroid, so there is no guarantee that each segment will have a particular area. E.g. cutting a 10001m² polygon by 100m may result in four 2500.25m² segments.

segment_by_grid

Arguments: [geometry: Geom, distance: Floating, align-to: Anything]

Returns: List[Geom]

Cut geometries up by a grid that has a mesh size of distance specified in metres (m). The grid is aligned with the to-align parameter which may be a point or a coverage (in which case the lower left corner is aligned to).

layer_intersection

Arguments: [feature: Anything, layer: Anything, merge_attributes: Nullable[Anything], return_difference: Nullable[Bool]]

Returns: List[{}]

Compute the intersection of a feature (a geometry-containing struct) with all of the features in another layer. The function returns a list of features that represent the intersection. If the function is given just two arguments, then the returned features are a copy of the original feature with just the geometry replaced by the intersection.

The third optional argument is a struct containing layer attributes to merge into the result. The identifier of each struct member is the new name the attribute will be given in the result. For example, if the given argument was {area_name: 'name', soil_type: soil_type} then this would merge in the soil_type attribute from the given layer, but rename the name attribute as area_name in the result.

The fourth optional return_difference argument determines what parts of the original geometry are included in the result. If false (the default), then only the geometry intersection is returned. If true, the intersection as well as any difference (non-overlapping geometry) is returned.

combine_coverages

Arguments: [coverages: Anything, grid-resolution: Floating]

Returns: Coverage[{}]

Combines multiple coverages into one. This lets you sample multiple hazard or resource-layers with a single operation.

For example, combine_coverages({shaking: event.shakemap, soil_type: resource_layer}, 100) would build a new coverage. Sampling the new coverage would return a struct. The attributes in this struct are the combined result of sampling the event.shakemap and resource_layer coverages respectively. The attributes names will be shaking and soil_type (or derivatives of these).

The first argument, coverages, must be a { key: value, ... } struct expression. Each value in this struct expression must be a coverage. Each key will be used for the attribute names in the result, when the combined coverage is sampled. If the individual coverage is raster data, the key name will be used exactly, e.g. shaking. If the individual coverage is vector data, the key name will be used as a prefix, e.g. shaking_pga. Note that the underlying vector-layer geometry is not returned at all.

The second grid-resolution argument specifies a common grid-size, in metres, to use across all the coverages. When the combined coverage is sampled using non-point geometry, the input geometry will be cut up using the given grid-resolution distance.

Cutting is done using the same method as the segment_by_grid() function. The grid-resolution should generally match the raster coverage with the smallest grid size.

Note that sampling intersections from a combined coverage only uses the centre point of each grid segment. Sampling may produce slightly different results at different grid-resolutions, especially when sampling a combined vector layer, and especially if the vector geometry is small, such as ballistics data.

Using a smaller grid-resolution will provide better accuracy when sampling vector data. However, a smaller grid-resolution will be much more processing intensive and make your model run slower.

map_coverage

Arguments: [coverage: Coverage[Anything], expression: λ(sampled)]

Returns: Coverage[Anything]

Applies an expression to transform the value(s) sampled from the given coverage. For example, this lets you convert the sampled value(s) from centimetres to metres, or from log-scale units to non-log units.

For example, to convert from centimetres to metres you would use: map_coverage(coverage, (x) -> x / 100)