def basic_flattening(target_folder, raster, res, origin, size, tin = False): """Reads some pre-determined vector files, tiles them using Lisa's code and "burns" them into the output raster. The flat elevation of the polygons is estimated by Laplace-interpolating at the locations of the polygon vertices. The underlying TIN is constructed from the centre points of the raster pixels. Rasterisation takes place via rasterio's interface. """ import startin from rasterio.features import rasterize from rasterio.transform import Affine transform = (Affine.translation(origin[0], origin[1]) * Affine.scale(size, size)) x0, x1 = origin[0] + size / 2, origin[0] + ((res[0] - 0.5) * size) y0, y1 = origin[1] + size / 2, origin[1] + ((res[1] - 0.5) * size) poly_fpaths = [ 'rest_bodies/bbg_rest_of_the_water.shp', 'sea_bodies/bbg_sea_and_big_bodies.shp', # You can add more resources here. ] wfs_urls = [ #('http://3dbag.bk.tudelft.nl/data/wfs', 'BAG3D:pand3d'), # You can add more resources here. ] in_vecs = [] for fpath in poly_fpaths: vec = vector_prepare([[x0, x1], [y0, y1]], target_folder + fpath) if len(vec) != 0: in_vecs.append(vec) for wfs in wfs_urls: vec = wfs_prepare([[x0, x1], [y0, y1]], wfs[0], wfs[1]) if len(vec) != 0: in_vecs.append(vec) if len(in_vecs) == 0: return if tin is False: xs, ys = np.linspace(x0, x1, res[0]), np.linspace(y0, y1, res[1]) xg, yg = np.meshgrid(xs, ys); xg = xg.flatten(); yg = yg.flatten() cs = np.vstack((xg, yg, raster.flatten())).transpose() data = cs[cs[:,2] != -9999] tin = startin.DT(); tin.insert(data) elevations = [] for polys in in_vecs: for poly, i in zip(polys, range(len(polys))): els = [] for vx in poly.exterior.coords: try: els += [tin.interpolate_laplace(vx[0], vx[1])] except: pass for interior in poly.interiors: for vx in interior.coords: try: els += [tin.interpolate_laplace(vx[0], vx[1])] except: pass elevations.append(np.median(els)) shapes = [] for polys in in_vecs: shapes += [(p, v) for p, v in zip(polys, elevations)] raspolys = rasterize(shapes, raster.shape, -9999, transform = transform) for yi in range(res[1]): for xi in range(res[0]): if raspolys[yi, xi] != -9999: raster[yi, xi] = raspolys[yi, xi] return tin
def __init__(self, input_tile: Tile, result_type: str): self._tile = input_tile directory = os.path.dirname(os.path.realpath(__file__)) config = configparser.ConfigParser() config.read(os.path.join(directory, "..", "config.ini")) self._to_overwrite = True if config["global"][ "overwrite_existing_files"] == "true" else False self._raster_cell_size = float( config["global"]["base_raster_cell_size"]) self._interpolation_variables = config[ "interpolation_dsm"] if result_type == "dsm" else config[ "interpolation_dtm"] self._stage = Stages.INTERPOLATED_DSM if result_type == "dsm" else Stages.INTERPOLATED_DTM tile_bounds = self._tile.get_unbuffered_geometry().bounds tile_bounds = [int(bound) for bound in tile_bounds] self._raster = None self._las_data = None self._tin = startin.DT() # [[minx, maxx], [miny, maxy]] self._extents = [[tile_bounds[0], tile_bounds[2]], [tile_bounds[1], tile_bounds[3]]] # Use width/height / raster cell size for x/y resolution self._resolution = [ int((tile_bounds[2] - tile_bounds[0]) // self._raster_cell_size), int((tile_bounds[3] - tile_bounds[1]) // self._raster_cell_size) ] # Origin = [minx, maxy] == topleft corner self._origin = [tile_bounds[0], tile_bounds[3]] self._y_range = reversed( np.arange(start=tile_bounds[1], stop=tile_bounds[1] + self._resolution[1] * self._raster_cell_size, step=self._raster_cell_size)) self._x_range = np.arange(start=tile_bounds[0], stop=tile_bounds[0] + self._resolution[0] * self._raster_cell_size, step=self._raster_cell_size) print('extents', self._extents) print('resolution', self._resolution) print('origin', self._origin)
def execute_startin(pts, res, origin, size, method): """Takes the grid parameters and the ground points. Interpolates either using the TIN-linear or the Laplace method. Uses a -9999 no-data value. Fully based on the startin package. """ import startin tin = startin.DT(); tin.insert(pts) ras = np.zeros([res[1], res[0]]) if method == 'startin-TINlinear': def interpolant(x, y): return tin.interpolate_tin_linear(x, y) elif method == 'startin-Laplace': def interpolant(x, y): return tin.interpolate_laplace(x, y) yi = 0 for y in np.arange(origin[1], origin[1] + res[1] * size, size): xi = 0 for x in np.arange(origin[0], origin[0] + res[0] * size, size): tri = tin.locate(x, y) if tri != [] and 0 not in tri: ras[yi, xi] = interpolant(x, y) else: ras[yi, xi] = -9999 xi += 1 yi += 1 return ras, tin
def utility_tin(pts, bounds, max_dh, max_angle, r, pts_inserted=None, seeds=None): """Utility function that can construct a TIN from the segmented point cloud of an NBRS. It needs either the preliminary edge estimates or the optimised edges to work (it controls the seeding of the initial TIN, and then its extension). In addition to using it to construct a TIN from points within the NBRS edges, the nbrs_manager class also uses it to optionally extend it with points outside the edges. The procedure still needs edges to be inserted into the TIN, but we do not wish to keep these in the TIN. However, point removal in startin does not seem to work reliably, so in each iteration the TIN is constructed anew, and the function returns a list containing inserted (and meaningful) points rather than the startin-based TIN itself. Arguments: - 'pts': the candidate points - 'bounds': the coordinates of a polygon that contains all candidate points - 'max_dh': TIN insertion elevation threshold (see the relevant nbrs_manager docstring for more) - 'max_angle': TIN insertion angle threshold (see the relevant nbrs_manager docstring for more) - 'pts_inserted': if extension is desired, then the points that were already inserted into the TIN, in the order in which they were previously inserted - 'seeds': the geometry from where the TIN construction is seeded, Lidar points very close to these points are inserted unconditionally """ # initialise KD-tree from input points, and TIN tree = cKDTree(pts) tree_len = tree.n tin = startin.DT() # if initial TIN (between NBRS edges) is being # built, then seed it by unconditionally # inserting points around the "skeleton" of # the polygon created from the NBRS edges if pts_inserted is None: pts_inserted = [] _, nbr_ixs = tree.query(seeds, 50, distance_upper_bound=1, workers=-1) stack = set(nbr_ixs.flatten()) - {tree_len} used, buffer = stack.copy(), [] while stack: pt = pts[stack.pop()] buffer += [pt] tin.insert_one_pt(*pt) pts_inserted.append(pt) # if this is not the first round (and extension # of a pre-existing TIN is desired), then first # re-construct the TIN from the already-inserted # points, then seed using the edges (or buffered # edges) from the last iteration else: for pti in pts_inserted: tin.insert_one_pt(*pti) pre_tree = cKDTree(pts_inserted) _, pre_ixs = pre_tree.query(seeds) # the seeds are the edges from the previous # iteration, and they are extruded to 3D using # the already-inserted points here seeds_z = [] for vx, bix in zip(seeds, pre_ixs): seeds_z.append((*vx[:2], pts_inserted[bix][2])) used, buffer = set(), seeds_z.copy() # insert the bounds into the TIN, keeping them at # a constant elevation of zero - keep track of their # TIN indices, so that they can be excluded from # elevation discrepancy computations later on bound_ixs = set() for bound_vx in bounds: bound_ixs.add(tin.insert_one_pt(*bound_vx)) # the outer iteration is based on a buffer, which # contains all points inserted in the previous iteration # of the outer loop - in the first iteration it contains # the seed points while buffer: # candidate points are fetched from the # neighbourhood of buffer points _, nbr_ixs = tree.query(buffer, 50, distance_upper_bound=r, workers=-1) # the inner loop is a stack-based one # all neighbours of buffer points are considered for # insertion, the variable "used" records which # points were inserted and should not be # considered again stack, buffer = set(nbr_ixs.flatten()) - {tree_len} - used, [] while stack: ix = stack.pop() pt = pts[ix] # get the triangle the candidate is located in # because all iterations of this function insert # a boundary encompassing all candidate points, # this operation is guaranteed to always work tri = tin.locate(*pt[:2]) # identify boundary points among the vertices # of the located triangle vx_ixs = np.array([tix for tix in tri if tix not in bound_ixs]) vxs = np.array([tin.get_point(tix) for tix in vx_ixs]) dh, grow = None, False # if the triangle has a boundary vertex among its vertices # then consider the operation a "growing" operation if len(vx_ixs) < 3: grow = True # else, consider it a "growing" operation only, if the # area or the circumference of the triangle indicates # that it probably does not belong to the road surface # (i.e. it has a large area or long circumference) else: cross = np.cross(vxs[1] - vxs[0], vxs[2] - vxs[0]) area = dist_topoint([0, 0, 0], cross) / 2 a = dist_topoint(vxs[0], vxs[1]) b = dist_topoint(vxs[1], vxs[2]) c = dist_topoint(vxs[2], vxs[0]) if area > 50 or a + b + c > 20: grow = True # if this is not a "growing" operation, interpolate # in the TIN to get the elevation deviation if not grow: dh = abs(pt[2] - tin.interpolate_laplace(*pt[:2])) # if this is a "growing" operation, then compute the # elevation difference as the mean difference relative # to the elevations of the TIN vertices, excluding the # boundary vertex # NOTE: this is the only way the algorithm can grow # the TIN beyond the current road surface extents elif len(vx_ixs) == 2: trs0 = tin.incident_triangles_to_vertex(vx_ixs[0]) trs1 = tin.incident_triangles_to_vertex(vx_ixs[1]) init_ixs = set(np.concatenate((trs0, trs1)).flatten()) nbr_trs = [ tin.incident_triangles_to_vertex(tix) for tix in list(init_ixs) ] nbr_ixs = set(np.concatenate(nbr_trs).flatten()) | init_ixs nbr_ixs = nbr_ixs - bound_ixs r_vxs = [tin.get_point(tix) for tix in list(nbr_ixs)] r_vxs = np.array([tin.get_point(tix) for tix in list(nbr_ixs)]) dh = dist_toplane(pt, *planefit_lsq(r_vxs)) # perform the angle test - if the triangle had a boundary # vertex among its vertices, ignore that vertex for the # purposes of the angle test (it is at zero elevation) if dh and dh < max_dh: insert = True for vx in vxs: dd = dist_topoint(pt[:2], vx[:2]) if not dd or abs(np.arctan(dh / dd)) > max_angle: insert = False break # if candidate passed both the elevation difference # and angle tests, then insert into TIN, add to the # buffer and mark as having been used already if insert: used.add(ix) buffer += [pt] tin.insert_one_pt(*pt) pts_inserted.append(pt) return pts_inserted
# import numpy as np # import matplotlib.pyplot as plt # from mpl_toolkits.mplot3d import Axes3D # import matplotlib.tri as mtri # from scipy.spatial import Delaunay # # tri = Delaunay(xyz_2) # # fig = plt.figure() # ax = fig.add_subplot(1, 1, 1, projection='3d') # ax.plot_trisurf(xyz_2[:,0], xyz_2[:,1], xyz_2[:,2], triangles=tri.simplices, cmap=plt.cm.Spectral) # plt.show() # ============================================================================= import startin dt = startin.DT() dt.insert(xyz_2) triangles = np.asarray(dt.all_triangles()) vertices = np.asarray(dt.all_vertices()) vertices = vertices[1:, :] threshold = 0.08 remove = [] for i in range(len(triangles)): tr = triangles[i, :] a = tr[0] b = tr[1] c = tr[2] a_cor = vertices[a - 1, :] b_cor = vertices[b - 1, :] c_cor = vertices[c - 1, :]
def filter_ground(jparams): """ Function that reads a LAS file, performs thinning, then performs ground filtering, and creates a two rasters of the ground points. One with IDW interpolation and one with TIN interpolation. Input: a dictionary jparams with all the parameters that are to be used in this function: - input-las: path to input .las file, - thinning-factor: thinning factor, ie. the `n` in nth point thinning method, - gf-cellsize: cellsize for the initial grid that is computed as part of the ground filtering algorithm, - gf-distance: distance threshold used in the ground filtering algorithm, - gf-angle: angle threshold used in the ground filtering algorithm, - idw-radius: radius to use in the IDW interpolation, - idw-power: power to use in the IDW interpolation, - output-las: path to output .las file that contains your ground classification, - grid-cellsize: cellsize of the output grids, - output-grid-tin: filepath to the output grid with TIN interpolation, - output-grid-idw: filepath to the output grid with IDW interpolation """ # load las file and relevant parameters point_cloud = File(jparams['input-las'], mode='r') scale = point_cloud.header.scale[0] print(point_cloud.header.min) print('- Flattening point cloud') gridded_pc = point_cloud_to_grid(point_cloud=point_cloud, tf=jparams['thinning-factor'], cell_size=int(jparams['gf-cellsize'] / scale)) ground_points, unprocessed_points, ll_origin = gridded_pc[0], gridded_pc[1], gridded_pc[2] print('- Growing terrain') dt = startin.DT() dt.insert(list(ground_points)) dt = grow_terrain(tin=dt, p=unprocessed_points, gp=ground_points, max_distance=int(jparams['gf-distance'] / scale), max_angle=jparams['gf-angle']) print('- Writing point cloud') with File(jparams['output-las'], mode='w', header=point_cloud.header) as out_file: gp = dt.all_vertices()[1:] out_file.X = [p[0] for p in gp] out_file.Y = [p[1] for p in gp] out_file.Z = [p[2] for p in gp] print('- Creating raster (TIN)\n\t- Interpolating (TIN)') dg = tin_interp(tin=dt, cell_size=int(jparams['grid-cellsize'] / scale)) print('\t- Writing Esri Ascii (TIN)') write_asc(grid=np.rot90(dg[0]) * scale + point_cloud.header.min[2], cell_size=jparams['grid-cellsize'], fn=jparams['output-grid-tin'], origin=(point_cloud.header.min[0]+dg[1][0]*scale, point_cloud.header.min[1] + dg[1][1]*scale), depth=2) print('- Creating raster (IDW)\n\t- Interpolating (IDW)') ig = idw_interp(tin=dt, cell_size=int(jparams['grid-cellsize'] / scale), radius=jparams['idw-radius'] / scale, power=jparams['idw-power']) print('\t- Writing Esri Ascii (IDW)') write_asc(grid=np.rot90(ig[0]) * scale + point_cloud.header.min[2], cell_size=jparams['grid-cellsize'], fn=jparams['output-grid-idw'], origin=(point_cloud.header.min[0]+ig[1][0]*scale, point_cloud.header.min[1]+ig[1][1]*scale), depth=2) return
def nn_interpolation(list_pts_3d, j_nn): """ !!! TO BE COMPLETED !!! Function that writes the output raster with nearest neighbour interpolation Input: list_pts_3d: the list of the input points (in 3D) j_nn: the parameters of the input for "nn" Output: returns the value of the area """ # print("cellsize:", j_nn['cellsize']) # -- to speed up the nearest neighbour us a kd-tree # https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.KDTree.html#scipy.spatial.KDTree # https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.KDTree.query.html#scipy.spatial.KDTree.query # kd = scipy.spatial.KDTree(list_pts) # d, i = kd.query(p, k=1) # find 4 boundaries ls_xy = [ ] # This is used for NN, if it is not necessary in other method. delete it ls_x = [] ls_y = [] ls_z = [ ] # This is used for NN, if it is not necessary in other method,delete it for i in range(len(list_pts_3d)): ls_x.append(list_pts_3d[i][0]) ls_y.append(list_pts_3d[i][1]) ls_xy.append((list_pts_3d[i][0], list_pts_3d[i][1])) ls_z.append(list_pts_3d[i][2]) bd_lf = min(ls_x) # left boundary, i.e XLLCORNER bd_rt = max(ls_x) # right boundary bd_lw = min(ls_y) # lower boundary, i.e YLLCORNER bd_up = max(ls_y) # upper boundary cellsize = j_nn['cellsize'] jparams = json.load(open('params.json')) jnn = jparams['nn'] # NROWS if (bd_up - bd_lw) % cellsize != 0: rows_num = int((bd_up - bd_lw) // cellsize + 1) else: rows_num = int((bd_up - bd_lw) // cellsize) # NNCOLS if (bd_rt - bd_lf) % cellsize != 0: cols_num = int((bd_rt - bd_lf) // cellsize + 1) else: cols_num = int((bd_rt - bd_lf) // cellsize) kd = scipy.spatial.KDTree(ls_xy) tin = startin.DT() tin.insert(list_pts_3d) with open(j_nn["output-file"], 'w+') as output: output.write("NCOLS " + str(cols_num) + "\n") output.write("NROWS " + str(rows_num) + "\n") output.write("XLLCORNER " + str(bd_lf) + "\n") output.write("YLLCORNER " + str(bd_lw) + "\n") output.write("CELLSIZE " + str(j_nn["cellsize"]) + "\n") output.write('NODATA_VALUE -9999' + "\n") for row in range(rows_num): ls_row = [] for col in range(cols_num): # point = cell_center(col, row, cellsize, bd_lf, bd_lw) center = (bd_lf + cellsize / 2 + col * cellsize, bd_lw + cellsize / 2 + (rows_num - 1 - row) * cellsize) # print('pt:',col,row,"cc:",point) if not tin.locate(center[0], center[1]): ls_row.append(str(-9999.0)) else: nn_pt_idx = kd.query([center], k=1)[1][0] nn_pt_z = list_pts_3d[nn_pt_idx][2] ls_row.append(str(nn_pt_z)) output.write(" ".join(ls_row) + '\n') print("File written to", j_nn['output-file'])
def kriging_interpolation(list_pts_3d, j_kriging): """ !!! TO BE COMPLETED !!! Function that writes the output raster with ordinary kriging interpolation Input: list_pts_3d: the list of the input points (in 3D) j_kriging: the parameters of the input for "kriging" Output: returns the value of the area """ # gaussian variogram def gaussian_vario(p1, p2): dt = distance(p1, p2) sill_gaussian = 1380 range_gaussian = 295 nugget_gaussian = 0 gamma = nugget_gaussian + sill_gaussian * ( 1.0 - math.exp(-9.0 * (dt**2) / (range_gaussian**2))) return gamma def weighted_average(coords, values, center): mat_cov = [] for pt1 in coords: row_vec = [] for pt2 in coords: row_vec.append(gaussian_vario(pt1, pt2)) mat_cov.append(row_vec + [1]) mat_cov.append([1] * len(coords) + [0]) mat_cov = numpy.array(mat_cov) mat_D = [] for i in coords: mat_D.append([gaussian_vario(i, center)]) mat_D.append([1]) mat_D = numpy.array(mat_D) try: mat_wt = numpy.matmul(numpy.linalg.inv(mat_cov), mat_D) except: return -9999.0 vec_wt = [w[0] for w in mat_wt[:-1]] vec_wt_normaalized = [w / sum(vec_wt) for w in vec_wt] if sum(vec_wt) else vec_wt return sum([val * w for val, w in zip(values, vec_wt_normaalized)]) cellsize = j_kriging['cellsize'] # data cleaning temp = [] [temp.append(i) for i in list_pts_3d if not i in temp] list_pts_3d = temp # find 4 boundaries ls_xy = [] ls_x = [] ls_y = [] ls_z = [] for i in range(len(list_pts_3d)): ls_x.append(list_pts_3d[i][0]) ls_y.append(list_pts_3d[i][1]) ls_xy.append((list_pts_3d[i][0], list_pts_3d[i][1])) ls_z.append(list_pts_3d[i][2]) bd_lf = min(ls_x) # left boundary, i.e XLLCORNER bd_rt = max(ls_x) # right boundary bd_lw = min(ls_y) # lower boundary, i.e YLLCORNER bd_up = max(ls_y) # upper boundary # NROWS if (bd_up - bd_lw) % cellsize != 0: rows_num = int((bd_up - bd_lw) // cellsize + 1) else: rows_num = int((bd_up - bd_lw) // cellsize) # NNCOLS if (bd_rt - bd_lf) % cellsize != 0: cols_num = int((bd_rt - bd_lf) // cellsize + 1) else: cols_num = int((bd_rt - bd_lf) // cellsize) tin = startin.DT() tin.insert(list_pts_3d) kd = scipy.spatial.KDTree(ls_xy) dc_pts_3d = dict(zip(ls_xy, ls_z)) with open(j_kriging["output-file"], 'w+') as output: output.write("NCOLS " + str(cols_num) + "\n") output.write("NROWS " + str(rows_num) + "\n") output.write("XLLCORNER " + str(bd_lf) + "\n") output.write("YLLCORNER " + str(bd_lw) + "\n") output.write("CELLSIZE " + str(int(j_kriging["cellsize"])) + "\n") output.write('NODATA_VALUE -9999' + "\n") for row in range(rows_num, 0, -1): ls_row = [] for col in range(1, cols_num + 1): # center = cell_center(col, row, float(cellsize), bd_lf, bd_lw) center = (bd_lf + cellsize * col - cellsize / 2, bd_lw + cellsize * row - cellsize / 2) if not tin.locate(center[0], center[1]): ls_row.append(str(-9999.0)) else: neighbours = kd.query_ball_point(center, j_kriging['radius']) cords = [ls_xy[i] for i in neighbours] vals = [ls_z[i] for i in neighbours] distaances = [ math.sqrt((center[0] - cord[0])**2 + (center[1] - cord[1])**2) for cord in cords ] if not neighbours: ls_row.append(str(-9999.0)) elif distaances.count(0): i = distaances.index(0) ls_row.append(str(vals[i])) else: ls_row.append( str(weighted_average(cords, vals, center))) output.write(" ".join(ls_row) + '\n') print("File written to", j_kriging['output-file'])
def tin_interpolation(list_pts_3d, j_tin): """ !!! TO BE COMPLETED !!! Function that writes the output raster with linear in TIN interpolation Input: list_pts_3d: the list of the input points (in 3D) j_tin: the parameters of the input for "tin" Output: returns the value of the area """ # -- example to construct the DT with scipy # https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Delaunay.html#scipy.spatial.Delaunay # dt = scipy.spatial.Delaunay([]) # -- example to construct the DT with startin # minimal docs: https://github.com/hugoledoux/startin_python/blob/master/docs/doc.md # how to use it: https://github.com/hugoledoux/startin_python#a-full-simple-example # you are *not* allowed to use the function for the tin linear interpolation that I wrote for startin # you need to write your own code for this step # but you can of course read the code [dt.interpolate_tin_linear(x, y)] # -- create lists for x,y,z values of sample points cellsize = j_tin['cellsize'] # data cleaning temp = [] [temp.append(i) for i in list_pts_3d if not i in temp] list_pts_3d = temp # find 4 boundaries ls_xy = [ ] # This is used for NN, if it is not necessary in other method. delete it ls_x = [] ls_y = [] ls_z = [ ] # This is used for NN, if it is not necessary in other method,delete it for i in range(len(list_pts_3d)): ls_x.append(list_pts_3d[i][0]) ls_y.append(list_pts_3d[i][1]) ls_xy.append((list_pts_3d[i][0], list_pts_3d[i][1])) ls_z.append(list_pts_3d[i][2]) bd_lf = min(ls_x) # left boundary, i.e XLLCORNER bd_rt = max(ls_x) # right boundary bd_lw = min(ls_y) # lower boundary, i.e YLLCORNER bd_up = max(ls_y) # upper boundary # NROWS if (bd_up - bd_lw) % cellsize != 0: rows_num = int((bd_up - bd_lw) // cellsize + 1) else: rows_num = int((bd_up - bd_lw) // cellsize) # NNCOLS if (bd_rt - bd_lf) % cellsize != 0: cols_num = int((bd_rt - bd_lf) // cellsize + 1) else: cols_num = int((bd_rt - bd_lf) // cellsize) raster_grid = numpy.zeros((rows_num, cols_num)) tin = startin.DT() tin.insert(list_pts_3d) for row in range(rows_num): for col in range(cols_num): center = (bd_lf + cellsize / 2 + col * cellsize, bd_lw + cellsize / 2 + (rows_num - 1 - row) * cellsize) # center = cell_center(col, row, cellsize, bd_lf, bd_lw) if not tin.locate(center[0], center[1]): raster_grid[row][col] = -9999 else: triangle = tin.locate(center[0], center[1]) pt1 = tin.get_point(triangle[0]) pt2 = tin.get_point(triangle[1]) pt3 = tin.get_point(triangle[2]) pt4 = [center[0], center[1]] a0 = abs((pt1[0] * (pt2[1] - pt3[1]) + pt2[0] * (pt3[1] - pt1[1]) + pt3[0] * (pt1[1] - pt2[1])) / 2) a1 = abs((pt4[0] * (pt2[1] - pt3[1]) + pt2[0] * (pt3[1] - pt4[1]) + pt3[0] * (pt4[1] - pt2[1])) / 2) a2 = abs((pt4[0] * (pt3[1] - pt1[1]) + pt3[0] * (pt1[1] - pt4[1]) + pt1[0] * (pt4[1] - pt3[1])) / 2) a3 = abs((pt4[0] * (pt1[1] - pt2[1]) + pt1[0] * (pt2[1] - pt4[1]) + pt2[0] * (pt4[1] - pt1[1])) / 2) raster_grid[row][col] = (a1 / a0) * pt1[2] + ( a2 / a0) * pt2[2] + (a3 / a0) * pt3[2] with open(j_tin['output-file'], 'w+') as output: output.write('NCOLS ' + str(cols_num) + '\n') output.write('NROWS ' + str(rows_num) + '\n') output.write("XLLCORNER " + str(bd_lf) + "\n") output.write("YLLCORNER " + str(bd_lw) + "\n") output.write("CELLSIZE " + str(j_tin["cellsize"]) + "\n") output.write('NODATA_VALUE -9999' + "\n") for row in range(rows_num): for col in range(cols_num): output.write(str(raster_grid[row][col]) + ' ') output.write('\n') print("File written to", j_tin['output-file'])
def nni_interpolation(list_pts_3d, j_nni): """ !!! TO BE COMPLETED !!! Function that writes the output raster with nni Input: list_pts_3d: the list of the input points (in 3D) j_nni: the parameters of the input for "nni" Output: returns the value of the area """ # find 4 boundaries ls_xy = [ ] # This is used for NN, if it is not necessary in other method. delete it ls_x = [] ls_y = [] ls_z = [ ] # This is used for NN, if it is not necessary in other method,delete it for i in range(len(list_pts_3d)): ls_x.append(list_pts_3d[i][0]) ls_y.append(list_pts_3d[i][1]) ls_xy.append((list_pts_3d[i][0], list_pts_3d[i][1])) ls_z.append(list_pts_3d[i][2]) bd_lf = min(ls_x) # left boundary, i.e XLLCORNER bd_rt = max(ls_x) # right boundary bd_lw = min(ls_y) # lower boundary, i.e YLLCORNER bd_up = max(ls_y) # upper boundary cellsize = j_nni['cellsize'] jparams = json.load(open('params.json')) jnni = jparams['nni'] # NROWS if (bd_up - bd_lw) % cellsize != 0: rows_num = int((bd_up - bd_lw) // cellsize + 1) else: rows_num = int((bd_up - bd_lw) // cellsize) # NNCOLS if (bd_rt - bd_lf) % cellsize != 0: cols_num = int((bd_rt - bd_lf) // cellsize + 1) else: cols_num = int((bd_rt - bd_lf) // cellsize) # kd = scipy.spatial.KDTree(ls_xy) tin = startin.DT() tin.insert(list_pts_3d) # dt = scipy.spatial.Delaunay(ls_xy, incremental=True) vd = scipy.spatial.Voronoi(ls_xy, incremental=True) # scipy.spatial.voronoi_plot_2d(vd, show_vertices=False) # plt.show() with open(j_nni["output-file"], 'w+') as output: output.write("NCOLS " + str(cols_num) + "\n") output.write("NROWS " + str(rows_num) + "\n") output.write("XLLCORNER " + str(bd_lf) + "\n") output.write("YLLCORNER " + str(bd_lw) + "\n") output.write("CELLSIZE " + str(j_nni["cellsize"]) + "\n") output.write('NODATA_VALUE -9999' + "\n") for row in range(rows_num): ls_row = [] for col in range(cols_num): # point = cell_center(col, row, cellsize, bd_lf, bd_lw) center = (bd_lf + cellsize / 2 + col * cellsize, bd_lw + cellsize / 2 + (rows_num - 1 - row) * cellsize) # print('pt:',col,row,"cc:",point) if not tin.locate(center[0], center[1]): ls_row.append(str(-9999.0)) else: in_ls_z = False for idx in range(len(ls_xy)): pt = ls_xy[idx] if abs(center[0] - pt[0]) < 0.00001 and abs( center[1] - pt[1]) < 0.00001: in_ls_z = ls_z[idx] break if in_ls_z: ls_row.append(str(in_ls_z)) else: ls_xy_plus = ls_xy[:] ls_xy_plus.append(center) # insert the center point in original vd vd_plus = scipy.spatial.Voronoi(ls_xy_plus) # insert the current center point in original dt dt_plue = scipy.spatial.Delaunay(ls_xy_plus) # get all neighbor vertices of center point dt_vnv = dt_plue.vertex_neighbor_vertices dt_center_vnv = dt_vnv[1][dt_vnv[0][-2]:dt_vnv[0][-1]] area = [] for region_center_idx in dt_center_vnv: boundary = [] # add vertices of intersection polygon between # each corresponding vd cell in vd and vd_plus vd_p_region = vd_plus.point_region[ region_center_idx] vd_p_region_boundary = [ idx for idx in vd_plus.regions[vd_p_region] ] vd_region = vd.point_region[region_center_idx] vd_region_boundary = [ idx for idx in vd.regions[vd_region] ] for vt in vd_p_region_boundary: in_boundary = False pt1 = vd_plus.vertices[vt] for pt2 in boundary: if abs(pt1[0] - pt2[0]) < 0.00001 and abs( pt1[1] - pt2[1]) < 0.00001: in_boundary = True if vt != -1 and vd_region_boundary.count( vt) == 0 and in_boundary is False: boundary.append(vd_plus.vertices[vt]) for vt in vd_region_boundary: in_boundary = False pt1 = vd.vertices[vt] for pt2 in boundary: if abs(pt1[0] - pt2[0]) < 0.00001 and abs( pt1[1] - pt2[1]) < 0.00001: in_boundary = True if vt != -1 and vd_p_region_boundary.count( vt) == 0 and in_boundary is False: boundary.append(vd.vertices[vt]) # create the ConvexHull of this intersection polygon # cannot form a ConvexHull (number of pt <= 2) if len(boundary) <= 2: # print(0, end=" ") area.append(0) # fig1 = scipy.spatial.voronoi_plot_2d(vd, show_vertices=False) # plt.show() # scipy.spatial.voronoi_plot_2d(vd_plus, show_vertices=False) # plt.show() else: # number of pt > 2 # detect if these vertices are collinear is_collinear = True slope_std = 0 if boundary[0][0] - boundary[-1][0] == 0: slope_std = sys.maxsize else: slope_std = ( boundary[0][1] - boundary[-1][1]) / ( boundary[0][0] - boundary[-1][0]) for i in range(1, len(boundary)): slope = 0 if boundary[0][0] - boundary[i][0] == 0: slope = sys.maxsize else: slope = ( boundary[0][1] - boundary[i][1] ) / (boundary[0][0] - boundary[i][0]) # print(slope, end=" ") if abs(slope - slope_std) > 0.00001: is_collinear = False break # print() # print("row: ", row, " col: ", col, boundary) if is_collinear is False: ch = scipy.spatial.ConvexHull(boundary) # print(ch.volume, end=" ") area.append(ch.volume) else: area.append(0) # calculate the weight of each neihbour point of center point # print() sum_area = sum(area) weight = [area_i / sum_area for area_i in area] # calculate the value of center point weighted_val = 0 if len(weight) != len(dt_center_vnv): print("wt:", weight) print("vnv: ", dt_center_vnv) for i in range(len(dt_center_vnv)): weighted_val += weight[i] * ls_z[dt_center_vnv[i]] ls_row.append(str(weighted_val)) output.write(" ".join(ls_row) + '\n') print("File written to", j_nni['output-file'])
y = np.delete(y, ground_points) z = np.delete(z, ground_points) xyz_lowest = np.vstack((x_lowest, y_lowest, z_lowest)).T #insert 4 extra point to create a convex hull that includes all points in the dataset xyz_outside = np.array( [[xmin - gf_cellsize, ymin - gf_cellsize, z_lowest[0]], [xmin - gf_cellsize, ymax + gf_cellsize, z_lowest[len(y_grid) - 2]], [ xmax + gf_cellsize, ymin - gf_cellsize, z_lowest[len(y_lowest) - (len(y_grid) - 1)] ], [xmax + gf_cellsize, ymax + gf_cellsize, z_lowest[len(y_lowest) - 1]]]) points_initial = np.concatenate((xyz_outside, xyz_lowest), axis=0) added = points_initial.shape[0] dt = startin.DT() dt.insert(points_initial) #list of remaining points: remaining = np.concatenate( (x.reshape(-1, 1), y.reshape(-1, 1), z.reshape(-1, 1)), axis=1) points_outside = [] k = 0 check = 0 while remaining.shape[0] > k: print(remaining.shape[0] - k) Triangle = dt.locate(remaining[k, 0], remaining[k, 1]) x_point0 = remaining[k, 0] y_point0 = remaining[k, 1]