Skip to main content
Geometries and Bodies
Updated over 8 months ago

Geometry overview

In reports, geometries are used to construct spatial queries such as finding all the trees within a 10m of a span.

In order to run network reports and export geo data such as GeoJSON and shapefiles, the schema needs a specified geometry field. This geometry field associates a report row (including the object and it's report fields) to a geographical location. Most schemas have a built-in geometry field.

In other cases, you can specify a custom geometry field

Geometry types

In Neara, there are two types of geometries. The most common one is Coordinate Distance Geometry which corresponds to GeoJSON geometries:

  • Point

  • Linestring

  • Polygon

The coordinates for these types are not exposed. As a result, these types are not directly modifiable or manipulatable. Instead, vector arithmetic can be performed by constructing vectors using vec2, vec3.

  • vec2(x,y) - vector in 2D space

  • vec3(x,y,z) - vector in 3D space

  • get_centroid(geometry) - returns the centroid of the geometry Vec2<Co-ord dist>

Vector functions

normalize(vec)

Returns a vector with the same direction as the specified vector and a magnitude of 1.

geoJSON

Vectors can be turned into geoJSON with these functions:

  • geo_point() - converts vec2/vec3 into Point

  • geo_linestring() - converts two or more vec2/vec3 into Linestring or Multi Linestring

  • geo_polygon() - converts three or more vec2/vec3 into Polygon or Multi Polygon

New geometries

New geometries can be created using these functions:

  • make_grid() - makes a grid

  • make_outline() - makes an outline polygon

Spatial and measurement queries

The following functions let you use geometries to do spatial and other measurement queries.

  • measure_distance() - finds the closest points between the two objects

  • geo_query() - queries a collection of geometric objects returning those that are within some distance of the given geometry, in order of closest to furthest

  • geo_intersect() - returns true if geom1 and geom2 intersect/overlap

  • convex_area() - returns the area of the convex hull of the given geometry

Catenaries

Catenaries are another type of geometries exposed in Neara. You can manipulate them using:

  • extend_catenary()

  • slice_catenaries()

Body functions

Body functions are used for more complex geometries such as 3D meshes specified in OBJ file formats.

To upload your KML, DXF or OBJ file into Neara, click the (+) button at the right hand end of the tabs in any section of the workspace, and select Data File under the Tools section in the popup.

Once you have imported your file, a Body can be created with the function load_bodies(). The required argument is the Data File that should be parsed. Other optional arguments are available for further customisation. See in-app documentation for more information.

To position the Body within your network, use transform_body(). This function takes in the loaded bodies, and returns a copy that has been translated and rotated according to the specified arguments.

Bodies can be visualised and used to as inputs to perform measurement calculations.

load_bodies

load_bodies(source, [namedArg:partition_layers, namedArg:terrain_wrap_height, namedArg:map_layers, namedArg: only_layers, namedArg:include_relative_geometries, namedArg:cull_backface])

The function returns a list of Body objects, derived from the source using the provided parameters. The source is either a (DXF or KML) data file or a blob ref.

  • partition_layers defaults to true. When set to false, all bodies will be merged into a single body with an empty layer name.

  • terrain_wrap_height determines the offset that is given to 2D primitives relative to the terrain. It will be set to 0 by default.

  • only_layers takes a list of layers, allowing them to be selectively included. When this is not specified all layers will be included.

  • map_layers takes a map, with keys and values both being strings, allowing layer names to be renamed before partitioning and filtering occurs. By default layer names will be unchanged.

  • terrain_wrap_increment specifies the maximum distance between adjacent vertices of 2D geometries, before additional vertices are inserted. If a value smaller than 3m is provided it will be interpreted as 3m.

  • extrude_geometry specifies how much vertical extrusion, generating an extruded skirt, instead of a flat polygonal body

  • include_relative_geometries specifies if relative height (2.5D) geometries should be included. If this is set to false, 2.5D geometries will be ignored. This is set to true by default.

  • cull_backface (advanced) defaults to a "smart" value, set to true or false to enable or disable "back face culling"

There exists a hard limit on the size of each body, being 65,536 unique vertices. If any of the constructed bodies exceeds this limit, no bodies will be created.

make_radial_body

make_radial_body(catenary_likes, [radius, radial_divisions, radial_increment, curve_increment_multiplier, color])

Makes an uncapped radial body around the given cables or catenaries

  • radius - the radius of the body around each curve

  • radial_divisions - the number of sides to the extruded regular polygon

  • radial_increment - arc length increment for the polygon (specify instead of radial_divisions)

  • curve_increment_multiplier - (advanced, defaults to 1.0). Change the increment along the curve for polygon rings

  • color - a string: an rgb hex color code to shade the body, without the # prefix. You can also use an rgb-alpha (RRGGBBAA) hex color code to create a translucent colored body.

transform_body

transform_body(body, [namedArg:translation, namedArg:localX, namedArg:localY, namedArg: localZ])

Returns a copy of the body, with its geometry transformed according to the provided arguments.

The translation argument provides the ability to shift the body by any arbitrary amount. This will default to no translation if not specified.

The localX, localY and localZ arguments perform a change of basis before the translation is applied, allowing any transform to be applied.

For convenience, logical defaults are selected when only a subset of the three vectors are specified. This includes:

  • When only localX/localY is provided, the remaining two vectors are set such that the transform is a rotation about the vertical (z) axis.

  • When only localZ is provided, the remaining two vectors are set such that the transform is a rotation about the X axis.

  • If the above is not applied, localX will default to vec3(1.0, 0.0, 0.0) if not specified, localY will default to vec3(0.0, 1.0, 0.0) if not specified, and localZ will default to vec3(0.0, 0.0, 1.0) if not specified.

Examples

Rotate 180 degrees about the Z axis

transform_body(body, localX: vec3(-1.0m, 0.0, 0.0))

Scale by 2

transform_body(localX: vec3(2.0m, 0.0, 0.0), localY: vec3(0.0m, 2.0m, 0.0), localZ: vec3(0.0, 0.0, 2.0m))

Align with assembly

transform_body(body, localX: normalize(vec3(longitudinal_direction.x, longitudinal_direction.y, 0.0m)), localY: normalize(vec3(longitudinal_direction.y, -longitudinal_direction.x, 0.0m)), localZ: vec3(0.0, 0.0, 1.0), translation: vec3(self.pole.location, self.pole.top_z))

Add a mesh on a pole

Upload a Data File with the name “test.obj”.

In a custom field on the Model, add the following function:

u_body = load_bodies(find(DataFiles, DataFiles[].name = "test.obj")

To shift the mesh to the top of the pole, we add the following custom field on the Pole:

transform_body(model().u_body, translation: vec3(location,top_z))


Function reference

vec2

vec2(x, y)

or

vec2(vec3)

Generates a 2D vector, given compatible-dimension components or a 3D vector. If built from a 3D vector it drops the Z component. The vector can have dimensional units and participates in dimensional analysis, much like scalars.

Example 1: 2D distance

  • vec2(3m, 4m)

  • vec2(3, 4) * 1m

Example 2: Absolute position

  • vec2(origin()) + vec2(200000m, 600000m)

vec3

vec3(x, y, z)

or

vec3(vec2, z)

Generates a 3D vector, given compatible-dimension components. A 2D vector can be substituted for x and y.

Example 1: 3D distance

  • vec3(3m, 4m, 5m)

  • vec3(3, 4, 5) * 1m

Example 2: Absolute position

  • origin() + vec3(200000m, 600000m, 40m)

normalize

normalize(vector)

Returns the normalized vector (dimensionless, length 1) unless the input is a zero vector in which case a zero vector is returned.

polyline3

polyline3(v1, v2, ...)

or

polyline3(list(v1, v2, ...))

Generates a 3D polyline. All the components must have absolute-coordinate dimensionality.

measure_distance

measure_distance(a, b)

Finds the closest points between the two objects and returns a distance measurement object.

The distance measurement object can be queried for a number of distance components and visualized.

If either a or b are lists, then returns the closest distance between anything in a to anything in b.

Example:

In a custom field on a Span, measure_distance(pole1, pole2) returns a distance measurement object between the two pole(s) of a span.

The distance object then has a number of useful fields, such as:

  • start, end which are the pair of closest points,

  • distance which returns the actual distance value,

  • horizontal_distance and vertical_distance which return the horizontal and vertical components of that distance,

Note that these last two are NOT necessarily the smallest such values, i.e. horizontal_distance is the horizontal component of the closest 3D distance, it is not the closest possible horizontal distance.

make_outline

make_outline(geometries, [concavity_radius: 20m, buffer: 10m])

Constructs an outline of the given geometries with buffer.

  • geometries: the geometry to outline.

  • concavity_radius: the inverse of the alpha parameter to an alpha shape routine.

  • buffer: extra points are automatically added to give thickness to lines and fill in long edges

extend_catenary

extend_catenary(catenary, location)

Extends the given catenary outwards from the closest endpoint so that it reaches the specified 2D location.

reproject

reproject(coord_data, [from_srid: srid, to_srid: srid])

Reprojects data between spatial references.

  • from: defaults to the current model srid

  • to: defaults to lng/lat

Example 1: Reproject a pole to WGS84 using implicit "from" SRID: reproject(pole.location, to: "4326")

Example 2: Reproject a pole to WGS84 using explicit "from" SRID: reproject(pole.location, from: model().srid, to: "4326")

make_grid

make_grid(cell_size: 500m, cover_model: true|false, cover_point_cloud: true|false)

Returns a grid of square polygons of the given size (world-aligned). Specify cover_model or cover_point_cloud to select the grid size.

Example:

Create a 100m world-aligned grid ensuring that every model object and every point is covered by a grid cell: make_grid(cell_size: 100m, cover_model: true, cover_point_cloud: true)

geo_linestring

geo_linestring(item1, item2, ...)

Creates a line string or multi line string, each item should be a list(vec2) or list(vec3).

Example 1: Create a LineString geometry on a Span:

geo_linestring(list(pole1.location, pole2.location))

Example 2: Create a MultiLineString on a StrainSection:

geo_linestring( list(index(Spans, 0).pole1.location, index(Spans, 0).pole2.location), list(index(Spans, 1).pole1.location, index(Spans, 1).pole2.location), )

Example 3: Create a LineString geometry directly:

geo_linestring(list( vec2(origin()) + vec2(200000m, 600000m), vec2(origin()) + vec2(200050m, 600000m), vec2(origin()) + vec2(200100m, 600000m), ))

Example 4: Create a MultiLineString geometry directly:

geo_linestring( list(vec2(origin()) + vec2(200000m, 600000m), vec2(origin()) + vec2(200000m, 600001m)), list(vec2(origin()) + vec2(200000m, 600000m), vec2(origin()) + vec2(200000m, 600000m)), )

geo_polygon

geo_polygon(item1, item2, ...)

Creates a polygon or multi polygon, each item is one polygon and should be a list(list(vec2)).

Example 1: Create a Polygon geometry with no hole:

geo_polygon(list(list( vec2(origin()) + vec2(200000m, 600000m), vec2(origin()) + vec2(200050m, 600000m), vec2(origin()) + vec2(200050m, 600050m), vec2(origin()) + vec2(200000m, 600000m), )))

Example 2: Create a Polygon geometry with one hole:

geo_polygon(list( list( // outer polygon vec2(origin()) + vec2(200000m, 600000m), vec2(origin()) + vec2(200050m, 600000m), vec2(origin()) + vec2(200050m, 600050m), vec2(origin()) + vec2(200000m, 600000m), ), list( // inner hole vec2(origin()) + vec2(200025m, 600025m), vec2(origin()) + vec2(200035m, 600025m), vec2(origin()) + vec2(200035m, 600035m), vec2(origin()) + vec2(200025m, 600025m), ), ))

get_centroid

get_centroid(object)

Returns the centroid of the given object/geometry.

geo_intersect

geo_intersect(geom1, geom2)

Returns true if geom1 and geom2 intersect/overlap.

  • On a span, geo_intersect(pole1.geometry, pole2.geometry) returns false

  • On a span, geo_intersect(geometry, pole1.geometry) returns true

get_outline_interval_points

get_outline_interval_points(geom, interval:10.0m)`

Returns a list of location vectors along the outline of the geometry at the given interval. \Point and MultiPoint are simply expanded into points.

Example:

get_outline_interval_points( geo_linestring( list(index(model().Spans, 0).pole1.location, index(model().Spans, 0).pole2.location), list(index(model().Spans, 1).pole1.location, index(model().Spans, 1).pole2.location), ), interval: 5m) 

returns a list of location vectors.

geo_query

geo_query(collection, geometry, distance)

Queries a collection of geometric objects returning those that are within some distance of the given geometry, in order of closest to furthest.

The second argument can either be a geometry or a type that has a geometry field.

For example, using pole is equivalent to using pole.geometry.

Example: Find all poles within 10m of the given pole using: geo_query(model().Poles, pole, 10m)

slice_catenaries

slice_catenaries(catenaries, [start_ratio: 0.0, end_ratio: 1.0, num_slices: 1])

Get the catenaries corresponding to the portions of the original catenaries within the specified ratio interval.

If start_ratio or end_ratio are outside the range [0.0, 1.0], the slices will be extended in to meet the specified ratio along their respective curves.

  • There is an option to slice the portion of catenaries into multiple slices using num_slices parameter.

  • Valid range for number of slices is [1, 100]

  • Catenaries with blowout are not currently supported; such catenaries will produce no slices.

Examples:

  • slice_catenaries(catenaries, start_ratio: 0.1, end_ratio: 0.9) returns the middle 80% of the specified catenaries.

  • slice_catenaries(catenaries, start_ratio: 0.2, end_ratio: 0.5 num_slices: 3) returns, for each catenary, a sequence of slices between the following points on the curve: 20%-30%-40%-50%

slice_line

slice_line(line, num_slices)

Slices line in equal parts. Limited to 100 slices.

convex_area

convex_area(geometry)

Returns the area of the convex hull of the given geometry

make_wedges

make_wedges(catenaries, angle, height, [downward: true])

Creates wedge-shaped volumes pointing either downward (if downward = true) or upward using the endpoints (instead of the curves) of catenaries, where the angle between the two sides of each wedge is [angle] and the triangular face of each wedge is [height] tall.

Example on a span:

make_wedges( Environments[].Cables, unit_value(75, "degrees"), 4m )

origin

origin()

Returns the coordinate origin as a Vector3 of absolute distance values.

Example: origin().z + 100m returns 100m of altitude as an absolute value.

Did this answer your question?