'''
This module contains functions used to generate paths and explore them.
'''
import numpy as np
import pandas as pd
import geopandas as gpd
def __segment(r0, c0, r1, c1, path, rcs):
'''
Creates a straight segment between two locations using Bresenham algorithm.
Parameters
----------
r0,c0: ints
row and column of origin
r1,c1: ints
row and column of end
path: 2D numpy array
current path
rcs: list
rows and columns of current path
Notes
-----
This is an internal function. First location is included in the segment but not the last one.
'''
# find row and column differences
delta_c = abs(c1 - c0)
delta_r = abs(r1 - r0)
# adjust signs depending on target's quadrant
if c0 < c1:
sign_c = 1
else:
sign_c = -1
if r0 < r1:
sign_r = 1
else:
sign_r = -1
e = delta_c - delta_r
r, c = r0, c0
while (r != r1) or (c != c1):
path[r, c] = 1
rcs = rcs + [[r,c]]
e2 = 2 * e
if e2 > -delta_r:
e -= delta_r
c += sign_c
if e2 < delta_c:
e += delta_c
r += sign_r
return path, rcs
[docs]def create_paths(blx, bly, origin, destinations, start_path=0):
'''
Creates a path to each destination.
Parameters
----------
blx: 2D numpy array
horizontal backlink
bly: 2D numpy array
vertical backlink
origin: list
list of origins [[row, colum],...]
destinations: list
list of destinations [[row, colum],...]
start_path: int
path identifier, optional
Returns
-------
paths: 2D numpy array
array that results from adding all paths
path_lst: list
a list of paths. Each path is represented by a dictionary containing three entries:
- 'destination': [row,col] of destination
- 'origin': [row, col] of origin
- 'track' : list containing two lists: [rows], [cols] for each cell making up the path
Notes
-----
Depending on the size of the chamfer window used, backlink arrays may contain jumps that are greater than one cell.
'''
# array to store path/s
paths = np.zeros_like(blx, dtype=np.int16)
# path number
path_num= start_path
# several destinations?
if len(destinations) > 1:
# initialize network paths dictionary
path_lst = []
for destination in destinations:
# array to store current path
pth = np.zeros_like(blx, dtype=np.uint16)
# row & columns of current path
rcs = []
# initialize first cell location
r0, c0 = destination
while (blx[r0, c0] != 0) or (bly[r0, c0] != 0):
# update to new path location
r1 = r0 + blx[r0, c0]
c1 = c0 + bly[r0, c0]
# add segment to new path location
pth, rcs = __segment(r0, c0, r1, c1, pth, rcs)
# update current location
r0, c0 = r1, c1
# add very last location
pth[r0, c0] = 1
rcs = rcs + [[r0, c0]]
# store path information
path_num += 1
path_lst.append({'id': path_num,
'origin': origin[0],
'destination': destination,
'track': np.array(rcs).T})
# add new path
paths += pth
return paths, path_lst
else: # only one destination
# array to store current path
pth = np.zeros_like(blx, dtype=np.uint16)
# row & columns of current path
rcs = []
# initialize first cell location
r0, c0 = destinations[0]
while (blx[r0, c0] != 0) or (bly[r0, c0] != 0):
# update to new path location
r1 = r0 + blx[r0, c0]
c1 = c0 + bly[r0, c0]
# add segment to new path location
pth, rcs = __segment(r0, c0, r1, c1, pth, rcs)
# update current location
r0, c0 = r1, c1
# add very last location
pth[r0, c0] = 1
rcs = rcs + [[r0, c0]]
# store path information
path_num += 1
path_dict = {'id': path_num,
'origin': origin[0],
'destination': destinations[0],
'track': np.array(rcs).T}
# add new path
paths += pth
return paths, path_dict
[docs]def path_stats(df_paths, ras, df, fun_dic={'fun':np.sum, 'name':'sum'}):
'''
Applies ``<function>`` to values in *ras* along each path in *df_paths* dataframe
Parameters
----------
df_paths: dataframe
contains information for various paths
ras: 2D numpy array
raster from where values are going to be extracted
df: dataframe
original dataframe with location information
fun_dic: dictionary
a dictionary with two entries:
- **'fun'**: a numpy function to compute values along a path track
- **'name'**: function name
Returns
-------
df_paths: dataframe
updated version of *df_paths* with an additional *name* column containing the results obtained
after applying *<function>* on *ras* values along each path.
Notes
-----
**df_paths** dataframe must contain a column with the path track (a 2D numpy array with the row and columns
that make up a path)
'''
# unpack fun
f= fun_dic['fun']
name = fun_dic['name']
# initialize variables
path_ids = []
path_stats = []
i=0
for _,pth in df_paths.iterrows():
# extract current path values
path_values = ras[pth['track'][0], pth['track'][1]]
# find origin and destination ids
sel = (df['r'] == pth['origin'][0]) & (df['c'] == pth['origin'][1])
o = df.loc[sel]['id'].values[0]
sel = (df['r'] == pth['destination'][0]) & (df['c'] == pth['destination'][1])
d = df.loc[sel]['id'].values[0]
# generate statistic
path_ids += [(o,d)]
path_stats += [f(path_values)]
# Update with new information
df_paths['path_ids'] = path_ids
df_paths[name] = path_stats
return df_paths[['id', 'path_ids', 'origin', 'destination', 'track', name]]