API

startinpy does not have specific classes and/or objects for points, vertices, and triangles. NumPy arrays of floats and integers are instead used.

A Point is an array of 3 floats (x-coordinate, y-coordinate, z-coordinate):

>>> import startinpy
>>> dt = startinpy.DT()
>>> dt.insert_one_pt([11.3, 22.2, 4.7])
>>> dt.points[1]
array([11.3, 22.2, 4.7])

A Vertex is an integer, it is the index in the array of points (startinpy.DT.points(), which is 0-based).

A Triangle is an array of 3 integers, the values of the indices of the 3 vertices (ordered counter-clockwise) in the array of points (startinpy.DT.points(), which is 0-based).

>>> dt.triangles[2]
array([1, 3, 2], dtype=uint64)
>>> #-- z-coordinate of 3rd vertex of the same triangle
>>> dt.points[dt.triangles[2][2]][2]
3.3

Important

The first vertex in the list of points is the infinite vertex, and has “infinity” coordinates ([inf, inf, inf]). It is used internally to ensure that the whole DT is consistent. No finite Triangle refers to the vertex, but infinite Triangles do. See more info and some examples.

class startinpy.DT(attributes_schema=None)

A Delaunay triangulation (DT), containing vertices+triangles

adjacent_triangles_to_triangle(t)

Return the triangles adjacent to Triangle t. Exception thrown if vertex index doesn’t exist in the DT.

Parameters:

t – the Triangle, an array of 3 vertex indices

Returns:

an array of 3 Triangles (finite and infinite)

>>> tris = dt.adjacent_triangles_to_triangle([1, 44, 23])
>>> for i, t in enumerate(tris):
>>>     print(i, t)    
0 [3 4 6]
1 [3 6 7]
2 [3 7 8]
adjacent_vertices_to_vertex(vi)

Return an array of vertex indices that are adjacent to vertex vi, that is those on the edges incident to vi. An exception is thrown if vi does not exist in the DT.

Parameters:

vi – the vertex index

Returns:

an array of vertex indices (ordered counter-clockwise)

area2d_triangle(t)

Calculate the area in 2D of a given triangle (projection xy-plane). An exception is thrown if the triangle doesn’t exist or is infinite.

Parameters:

t – the Triangle, an array of 3 vertex indices

Returns:

the area in 2D

>>> dt.area2d_triangle([34, 21, 1])
22.1
area3d_triangle(t)

Calculate the area in 3D of a given triangle. An exception is thrown if the triangle doesn’t exist or is infinite.

Parameters:

t – the Triangle, an array of 3 vertex indices

Returns:

the area in 3D

>>> dt.area3d_triangle([34, 21, 1])
32.2
attributes

Get all the values for a given extra attribute stored for the vertices. Returns the values as a numpy structured array. Watch out, if a given vertex doesn’t have a specific attribute then np.nan is inserted for f64, max-values for i64 and u64, “” for String, 0 for bool.

Returns:

an NumPy array with all the values (including for the removed vertices and the infinite vertex). The array is empty if the extra attributes don’t exist.

>>> dt = startinpy.DT()
>>> dt.add_attribute_map(np.dtype([("classification", np.uint64)]))
>>> dt.insert_one_pt([85000.0, 444003.2, 2.2], classification=6)
>>> ...
>>> dt.attributes[1:]
array([6, 2, 6, 6, ..., 6, 9])
closest_point(p2)

Return the closest vertex index to [x, y] (distance in 2D). An Exception is thrown if [x, y] is outside the convex hull.

Parameters:

p2 – array with [x, y]-coordinates of point

Returns:

the vertex index of the closest point

>>> try:
>>>     cp = dt.closest_point([32.1, 66.9])
>>> except Exception as e:
>>>     print(e)
collect_garbage()

Collect garbage, that is remove from memory the vertices marked as removed (modifies the array dt.points and all indices of the triangles).

Watch out: the vertices get new IDs, and thus the triangles get updated too. And this can be a slow operation.

>>> if dt.has_garbage():
>>>     dt.collect_garbage()
>>> assert (dt.has_garbage() == False)
convex_hull()

Return the convex hull as an array of vertex indices.

Returns:

an array of vertex indices, oriented counter-clockwise (CCW)

>>> dt.convex_hull()
array([2, 13, 4, 51, 27], dtype=uint64)
duplicates_handling

Specify the method to handle xy-duplicates. That is, if the insertion of a new point in the DT is impossible because a vertex already exists (based on startinpy.DT.snap_tolerance()), then we can decide which z-value we want to keep in the DT. There are 4 options:

  1. “First” (default): the z-value of the first point inserted at that xy-location is kept

  2. “Last”: the z-value of the last point inserted at that xy-location is kept

  3. “Lowest”: the lowest z-value is kept

  4. “Highest”: the highest z-value is kept

>>> dt = startinpy.DT()
>>> dt.duplicates_handling = "Highest"
get_attributes_schema()

Get the attribute schema that contains the data type definitions for the extra attributes (if any).

Returns:

a NumPy Data type object (dtype)

>>> d = np.dtype([('classification', np.float64), ('name', '<U8')])
>>> dt.set_attributes_schema(d)
True
>>> dt.get_attributes_schema()
[('classification', '<f8'), ('name', '<U8')]
get_bbox()

Return the bbox of the dataset

Returns:

an array of 4 coordinates: [minx, miny, maxx, maxy]

>>> bbox = dt.get_bbox()
array([ 0., 0., 10., 12. ])
get_point(vi)

Return the point for the vertex with index vi. An exception is thrown if the vertex index is invalid.

Parameters:

vi – the index of the vertex

Returns:

the point

>>> v = dt.get_point(4)
array([13., 2.0, 11.])
get_vertex_attributes(vi)

Get all the extra attributes stored for a specific vertex. Returns the values as a JSON dictionary in string. An exception is thrown if the terrain has no extra attributes stored and/or if the vertex index is invalid.

Parameters:

vi – the index of the vertex

Returns:

a JSON object

>>> dt.get_vertex_attributes(17)
{'intensity': 111.1, 'reflectance': 99.1}
has_garbage()

Returns True if some vertices are marked as to be deleted (but still in memory) , False otherwise.

Returns:

True/False

incident_triangles_to_vertex(vi)

Return the triangles incident to vertex vi. Infinite triangles are also returned. Exception thrown if vertex index doesn’t exist in the DT or if it has been removed.

Parameters:

vi – the vertex index

Returns:

an array of triangles (ordered counter-clockwise)

>>> trs = dt.incident_triangles_to_vertex(3)
>>> for i, t in enumerate(trs):
>>>     print(i, t)    
0 [3 4 6]
1 [3 6 7]
2 [3 7 8]
3 [3 8 2]
4 [3 2 9]
5 [3 9 4]
insert(pts, insertionstrategy='AsIs')

Insert each point in the array of points (a 2D array) by calling insert_one_pt() for each. Different insertion strategies can be used: “AsIs” (default: inserts points in the order given) or “BBox” (inserts first the BBox of the points, which speeds up the construction, works especially good for rasters).

Parameters:
  • pts – an array of points (which is itself an array)

  • insertionstrategy (optional) – “AsIs” (default) or “BBox”

Returns:

(nothing)

>>> pts = []
>>> pts.append([1.0, 1.0, 11.11])
>>> pts.append([1.0, 2.3, 22.22])
>>> pts.append([12.3, 21.0, 4.52])
>>> ...
>>> dt = startinpy.DT()
>>> dt.insert(pts)
OR
>>> dt.insert(pts, insertionstrategy="BBox")
insert_one_pt(p3, **py_kwargs)

Insert one new point in the DT.

If there is a point at the same location (based on startinpy.DT.snap_tolerance()), then startinpy.DT.duplicates_handling() decides which z-value (and eventually extra attributes) are kept.

Parameters:
  • p3 – array with [x, y, z]-coordinates of point to insert

  • attributes (optional extra) – extra parameters with values

Returns:

a tuple containing: 1) the index of the (created or kept) vertex in the triangulation; 2) whether a new vertex was inserted: True if yes; False is there was already a vertex at that xy-location. 3) whether the z/attributes were updated or not, based on the startinpy.DT.duplicates_handling()

>>> (vi, bNewVertex, bZUpdated) = dt.insert_one_pt([3.2, 1.1, 17.0])
(37, True)
>>> dt.insert_one_pt([13.2, 44.1, 74.2], intensity=77.2)
interpolate(interpolant, locations, strict=False)

Estimate the z-value with 5 different spatial interpolation methods:

  1. IDW: inverse distance weighing

  2. Laplace: a faster NNI with almost the same results

  3. NN: nearest neighbour

  4. NNI: natural neighbour interpolation

  5. TIN: linear interpolation in TIN

The interpolation does not modify the triangulation, it only returns an estimation for the z-values at the xy-location provided as argument.

Parameters:
  • interpolant – a JSON/dict Python object with a “method”: “IDW” (or others). IDW has 2 more params: “power” and “radius”; NNI can have the Voronoi cells precomputed with “precompute”

  • locations – an array of [x, y] locations where the function should interpolate

  • strict – (default=False) if the interpolation cannot find a value (because outside convex hull or search radius too small) then strict==True will stop at the first error and return that error. If strict==False then numpy.nan is returned.

Returns:

a numpy array containing all the interpolation values (same order as input array). numpy.nan when location is outside the convexhull of the DT.

>>> locs = [ [50.0, 41.1], [101.1, 33.2], [80.0, 66.0] ]
>>> re = dt.interpolate({"method": "NNI", "precompute": True}, locs)
>>> re = dt.interpolate({"method": "Laplace"}, locs)
>>> re = dt.interpolate({"method": "IDW", "radius": 20, "power": 2.0}, locs, strict=True)
is_finite(t)

Verify whether a Triangle is finite, or not. An infinite triangle has the first 0-vertex as one of its vertices. This doesn’t verify wether the triangle exists (use is_triangle()).

Parameters:

t – the Triangle, an array of 3 vertex indices

Returns:

True if t is finite, False is infinite

>>> re = dt.is_finite(np.array([11, 162, 666])))
is_inside_convex_hull(p2)

Is the point [x, y] located inside the convex hull of the DT.

Parameters:

p2 – array with [x, y]-coordinates of point to test

Returns:

True if [x,y] is inside the convex hull or on its boundary, False otherwise.

is_triangle(t)

Verify if a triangle exists in the DT.

Parameters:

t – the Triangle, an array of 3 vertex indices

Returns:

True if t exists, False otherwise

>>> dt.is_triangle(np.array([11, 162, 66]))
False
is_vertex_convex_hull(vi)

Return True if vertex vi is on the boundary of the convex hull, False otherwise.

Parameters:

vi – the vertex index

Returns:

True if vi is on the boundary of the convex hull, False otherwise. Also False is returned if the vertex doesn’t exist in the DT.

is_vertex_removed(vi)

Return True if vertex vi is labelled as removed, False otherwise.

Parameters:

vi – the vertex index

Returns:

True if vi is labelled as removed, False otherwise. An exception is raised if vi doesn’t exist.

jump_and_walk

Activate/deactivate the jump-and-walk for the point location. If deactivated the walk starts from the last inserted triangle; this is the default and should work fine for most real-world datasets. If activated, then before a walk (to insert a new points in the DT), a subset of the points (hard-coded value: n^0.25) are sampled, the Euclidean distance to each are calculated, and the walk starts from the closest one. This should be activated when the spatial coherence in the dataset is very low (ie if the points are randomly shuffled). (default=False)

>>> dt = startinpy.DT()
>>> dt.jump_and_walk = True
locate(p2)

Locate the triangle containing the point [x, y] (projected to 2D). An error is thrown if it is outside the convex hull.

Parameters:

p2 – array with [x, y]-coordinates of point

Returns:

the triangle

>>> tr = dt.locate([34.2, 55.6])
array([65, 61, 23], dtype=uint64)
normal_triangle(t)

Calculate the normal of a given triangle. An exception is thrown if the triangle doesn’t exist or is infinite.

Parameters:

t – the Triangle, an array of 3 vertex indices

Returns:

a Vec with 3 values: nx, ny, nz (normalised normal)

>>> dt.normal_triangle([17, 451, 22])
>>>
array([15.63303377, 26.9968598 ,  23.4])
normal_vertex(vi)

Calculate the normal of a given vertex. An exception is thrown if the vertex index is invalid.

Parameters:

vi – the index of the vertex

Returns:

a Vec with 3 values: nx, ny, nz (normalised normal)

>>> dt.normal_vertex(17)
>>> dt.points[17]
array([15.63303377, 26.9968598 ,  23.4])
number_of_triangles()
Returns:

number of finite triangles

number_of_vertices()
Returns:

number of finite vertices

points

Get the points [x, y, z] of all vertices in the DT. This includes the infinite vertex (vertex at position 0), which is not part of the DT (no finite Triangle reference it, but infinite Triangles do)

>>> pts = dt.points
>>> print(pts.shape) #-- this is a numpy array
(102, 3)
>>> for p in pts:
>>>     print(p[0], p[1], p[2])
...
>>> dt.points[27]
array([101.122, 72.293, 11.223])
>>> dt.points[0]
array([inf, inf, inf])
remove(vi)

Remove/delete the vertex vi (an index) from the DT, and update the DT for the Delaunay criterion.

Parameters:

vi – index of vertex to delete

Returns:

(Exception is thrown if vi is invalid)

>>> try:
>>>     t.remove(45)
>>> except Exception as e:
>>>     print(e)
set_attributes_schema(dtype)

Set the attribute schema (the definition of the data type) for the extra attributes that each vertex can store.

If that function is used, all previous stored attributes and schema will be removed.

Adding attributes to a triangulation that has no schema will result in no attributes stored, only those compliant with the schema are stored.

Only the following data types for each attribute are allowed: ‘numpy.bool_’, ‘numpy.int64’, ‘numpy.uint64’, unicode (string), ‘numpy.float64’.

Parameters:

dtype – a NumPy Data type object (dtype)

Returns:

True if the schema is valid, otherwise an error is thrown.

>>> dt = startinpy.DT()
>>> myschema = np.dtype([('classification', np.uint64), ('intensity', float)])
>>> dt.set_attributes_schema(myschema)
>>> dt.insert_one_pt([85000.0, 444003.2, 2.2], classification=2, intensity=111.1)
set_vertex_attributes(vi, **py_kwargs)

Add new attributes to a vertex (even if it already has some). Returns the values as a JSON dictionary in string.

Parameters:
  • vi – the index of the vertex

  • extra_attributes (optional) – extra parameters with values

Returns:

True if the attribute was assigned, False otherwise

>>> dt.insert_one_pt([85000.0, 444003.2, 2.2], intensity=111.1, reflectance=29.9)
>>> ...
>>> dt.set_vertex_attributes(17, classification=2)
>>> dt.get_vertex_attributes(17)
{'intensity': 111.1, 'reflectance': 29.9, 'classification': 2, }'    
snap_tolerance

Get/set the snap tolerance used to merge vertices during insertion. Two vertices closer than this value (calculated in the xy-plane) will be merged during insertion. The z-value preserved for that vertex is based on the startinpy.DT.duplicates_handling(). (default=0.001)

>>> dt = startinpy.DT()
>>> dt.snap_tolerance = 0.05 #-- modify to 0.05unit
>>> print("The snap tolerance is:", dt.snap_tolerance)
The snap tolerance is: 0.05
triangles

Get the triangles in the DT (only finite triangles).

>>> trs = dt.triangles
>>> print(trs.shape)
(224, 3) #-- this is a numpy array
>>> one_triangle = trs[22]
>>> first_vertex = one_triangle[0]
>>> print("x-coordinate of first vertex: ", dt.points[first_vertex])
x-coordinate of first vertex: [25.98 35.12 4.78]
update_vertex_z_value(vi, z)

Update/set the z-value for a specific vertex. An exception is thrown if the vertex index is invalid.

Parameters:
  • vi – the index of the vertex

  • z – the new z-value/elevation

Returns:

True if the attribute was assigned, False otherwise

>>> dt.update_vertex_z_value(17, 23.4)
>>> dt.points[17]
array([15.63303377, 26.9968598 ,  23.4])
vertical_exaggeration(factor)

Vertically exaggerate the elevation values of the vertices. Used mostly for visualisation.

Parameters:

factor – a positive value (can be <1.0 to remove exaggeration)

Returns:

(nothing)

>>> dt.vertical_exaggeration(2.0)
>>> dt.vertical_exaggeration(0.5)
volume_triangle(t, zplane=0.0)

Calculate the volume of a given triangle wrt to a base z-plane. An exception is thrown if the triangle doesn’t exist or is infinite.

Parameters:
  • t – the Triangle, an array of 3 vertex indices

  • zplane – (default=0.0)the z-value of the base plane

Returns:

the signed volume in 3D

>>> dt.volume_triangle([34, 21, 1], 10.0)
32.2
write_cityjson(path, digits=3)

Write a CityJSON file of the DT (vertices+triangles) to the path (a string). One TINRelief object is created. Throws an exception if the path is invalid.

Parameters:
  • path – full path (a string) on disk of the file to create (will overwrite)

  • digits – (default=3) number of digits to keep (for saving efficiently the coordinates)

Returns:

(nothing)

>>> dt.write_cityjson("/home/elvis/myfile.city.json")
write_geojson(path)

Write a GeoJSON file of the DT (vertices+triangles) to the path (a string). Throws an exception if the path is invalid.

Parameters:

path – full path (a string) on disk of the file to create (will overwrite)

Returns:

(nothing)

>>> dt.write_geojson("/home/elvis/myfile.geojson")
write_obj(path)

Write an OBJ of the DT to the path (a string). Throws an exception if the path is invalid.

Parameters:

path – full path (a string) on disk of the file to create (will overwrite)

Returns:

(nothing)

>>> dt.write_obj("/home/elvis/myfile.obj")
write_ply(path)

Write an PLY of the DT to the path (a string). Throws an exception if the path is invalid.

Parameters:

path – full path (a string) on disk of the file to create (will overwrite)

Returns:

(nothing)

>>> dt.write_ply("/home/elvis/myfile.ply")