def bash_matrix_od(origins, destinationShp, network, costCol, oneway, grsWork, output): """ Produce matrix OD using GRASS GIS - BASH MODE """ from gasp.session import run_grass from gasp.oss import get_filename from gasp.oss.ops import create_folder from gasp.mng.split import splitShp_by_range from gasp.mng.gen import merge_feat # SPLIT ORIGINS IN PARTS originsFld = create_folder(os.path.join(grsWork, 'origins_parts')) originsList = splitShp_by_range(origins, 100, originsFld) # Open an GRASS GIS Session gbase = run_grass(grsWork, grassBIN="grass76", location=grsLoc, srs=network) import grass.script as grass import grass.script.setup as gsetup RESULTS = [] R_FOLDER = create_folder(os.path.join(grsWork, 'res_parts')) for e in range(len(originsList)): gsetup.init(gbase, grsWork, "grs_loc_{}".format(e), 'PERMANENT') from gasp.to.shp.grs import shp_to_grs, grs_to_shp # Add Data to GRASS GIS rdvMain = shp_to_grs(network, get_filename(network, forceLower=True)) # Produce Matrix result_part = prod_matrix(originsList[e], destinationShp, rdvMain, costCol, oneway) # Export Result shp = grs_to_shp(result_part, os.path.join(R_FOLDER, result_part + '.shp'), geom_type="line", lyrN=3) RESULTS.append(shp) merge_feat(RESULTS, output, api='pandas') return output
def prod_matrix(origins, destinations, networkGrs, speedLimitCol, onewayCol, thrdId="1", asCmd=None): """ Get Matrix Distance: """ from gasp.to.shp.grs import shp_to_grs from gasp.cpu.grs.mng import category from gasp.anls.exct import sel_by_attr from gasp.mng.grstbl import add_field, add_table, update_table from gasp.mob.grstbx import network_from_arcs from gasp.mob.grstbx import add_pnts_to_network from gasp.mob.grstbx import run_allpairs from gasp.cpu.grs.mng.feat import geomattr_to_db from gasp.cpu.grs.mng.feat import copy_insame_vector from gasp.mng.gen import merge_feat from gasp.prop.feat import feat_count # Merge Origins and Destinations into the same Feature Class ORIGINS_NFEAT = feat_count(origins, gisApi='pandas') DESTINATIONS_NFEAT = feat_count(destinations, gisApi='pandas') ORIGINS_DESTINATIONS = merge_feat([origins, destinations], os.path.join( os.path.dirname(origins), "points_od_{}.shp".format(thrdId)), api='pandas') pointsGrs = shp_to_grs(ORIGINS_DESTINATIONS, "points_od_{}".format(thrdId), asCMD=asCmd) # Connect Points to Network newNetwork = add_pnts_to_network(networkGrs, pointsGrs, "rdv_points_{}".format(thrdId), asCMD=asCmd) # Sanitize Network Table and Cost Columns newNetwork = category(newNetwork, "rdv_points_time_{}".format(thrdId), "add", LyrN="3", geomType="line", asCMD=asCmd) add_table(newNetwork, ("cat integer,kph double precision,length double precision," "ft_minutes double precision," "tf_minutes double precision,oneway text"), lyrN=3, asCMD=asCmd) copy_insame_vector(newNetwork, "kph", speedLimitCol, 3, geomType="line", asCMD=asCmd) copy_insame_vector(newNetwork, "oneway", onewayCol, 3, geomType="line", asCMD=asCmd) geomattr_to_db(newNetwork, "length", "length", "line", createCol=False, unit="meters", lyrN=3, ascmd=asCmd) update_table(newNetwork, "kph", "3.6", "kph IS NULL", lyrN=3, ascmd=asCmd) update_table(newNetwork, "kph", "3.6", "oneway = 'N'", lyrN=3, ascmd=asCmd) update_table(newNetwork, "ft_minutes", "(length * 60) / (kph * 1000.0)", "ft_minutes IS NULL", lyrN=3, ascmd=asCmd) update_table(newNetwork, "tf_minutes", "(length * 60) / (kph * 1000.0)", "tf_minutes IS NULL", lyrN=3, ascmd=asCmd) # Exagerate Oneway's update_table(newNetwork, "ft_minutes", "1000", "oneway = 'TF'", lyrN=3, ascmd=asCmd) update_table(newNetwork, "tf_minutes", "1000", "oneway = 'FT'", lyrN=3, ascmd=asCmd) # Produce matrix matrix = run_allpairs(newNetwork, "ft_minutes", "tf_minutes", 'result_{}'.format(thrdId), arcLyr=3, nodeLyr=2, asCMD=asCmd) # Exclude unwanted OD Pairs q = "({}) AND ({})".format( " OR ".join( ["from_cat={}".format(str(i + 1)) for i in range(ORIGINS_NFEAT)]), " OR ".join([ "to_cat={}".format(str(ORIGINS_NFEAT + i + 1)) for i in range(DESTINATIONS_NFEAT) ])) matrix_sel = sel_by_attr(matrix, q, "sel_{}".format(matrix), geomType="line", lyrN=3, asCMD=asCmd) add_field(matrix_sel, "from_fid", "INTEGER", lyrN=3, asCMD=asCmd) add_field(matrix_sel, "to_fid", "INTEGER", lyrN=3, asCMD=asCmd) update_table(matrix_sel, "from_fid", "from_cat - 1", "from_fid IS NULL", lyrN=3, ascmd=asCmd) update_table(matrix_sel, "to_fid", "to_cat - {} - 1".format(str(ORIGINS_NFEAT)), "to_fid IS NULL", lyrN=3, ascmd=asCmd) return matrix_sel
def thrd_matrix_od(origins, destinationShp, network, costCol, oneway, grsWork, grsLoc, output): """ Produce matrix OD using GRASS GIS - Thread MODE PROBLEM: * O programa baralha-se todo porque ha muitas sessoes do grass a serem executadas. E preciso verificar se e possivel segregar as varias sessoes do grass """ from threading import Thread from gasp.session import run_grass from gasp.oss import get_filename from gasp.oss.ops import create_folder from gasp.mng.split import splitShp_by_range from gasp.mng.gen import merge_feat # SPLIT ORIGINS IN PARTS originsFld = create_folder(os.path.join(grsWork, 'origins_parts')) originsList = splitShp_by_range(origins, 100, originsFld) gbase = run_grass(grsWork, grassBIN="grass74", location=grsLoc, srs=network) import grass.script as grass import grass.script.setup as gsetup gsetup.init(gbase, grsWork, grsLoc, 'PERMANENT') from gasp.to.shp.grs import shp_to_grs from gasp.to.shp.grs import grs_to_shp # Add Data to GRASS GIS rdvMain = shp_to_grs(network, get_filename(network, forceLower=True), asCMD=True) RESULTS = [] R_FOLDER = create_folder(os.path.join(grsWork, 'res_parts')) def __prod_mtxod(O, D, THRD): result_part = prod_matrix(O, D, rdvMain, costCol, oneway, thrdId=THRD, asCmd=True) shp = shp_to_grs(result_part, os.path.join(R_FOLDER, result_part + '.shp'), geom_type="line", lyrN=3, asCMD=True) RESULTS.append(shp) thrds = [] for i in range(len(originsList)): thrds.append( Thread(name='tk-{}'.format(str(i)), target=__prod_mtxod, args=(originsList[i], destinationShp, str(i)))) for t in thrds: t.start() for t in thrds: t.join() merge_feat(RESULTS, output, api='pandas') return output
def vector_based(osmdata, nomenclature, refRaster, lulcShp, overwrite=None, dataStore=None, RoadsAPI='SQLITE'): """ Convert OSM Data into Land Use/Land Cover Information An vector based approach. TODO: Add a detailed description. RoadsAPI Options: * SQLITE * POSTGIS """ # ************************************************************************ # # Python Modules from Reference Packages # # ************************************************************************ # import datetime import os import json # ************************************************************************ # # GASP dependencies # # ************************************************************************ # from gasp.oss.ops import create_folder from gasp.prop.rst import get_epsg_raster from gasp.session import run_grass if RoadsAPI == 'POSTGIS': from gasp.sql.mng.db import create_db from gasp.osm2lulc.utils import osm_to_pgsql else: from gasp.osm2lulc.utils import osm_to_sqdb from gasp.osm2lulc.utils import osm_project, add_lulc_to_osmfeat from gasp.mng.gen import merge_feat from gasp.osm2lulc.mod1 import grs_vector if RoadsAPI == 'SQLITE' or RoadsAPI == 'POSTGIS': from gasp.osm2lulc.mod2 import roads_sqdb else: from gasp.osm2lulc.mod2 import grs_vec_roads from gasp.osm2lulc.m3_4 import grs_vect_selbyarea from gasp.osm2lulc.mod5 import grs_vect_bbuffer from gasp.osm2lulc.mod6 import vector_assign_pntags_to_build # ************************************************************************ # # Global Settings # # ************************************************************************ # if not os.path.exists(os.path.dirname(lulcShp)): raise ValueError('{} does not exist!'.format(os.path.dirname(lulcShp))) # Get Parameters to connect to PostgreSQL conPGSQL = json.load( open( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'con-postgresql.json'), 'r')) if RoadsAPI == 'POSTGIS' else None # Get EPSG of Reference Raster epsg = get_epsg_raster(refRaster) if not epsg: raise ValueError('Cannot get epsg code of ref raster') time_a = datetime.datetime.now().replace(microsecond=0) from gasp.osm2lulc.var import osmTableData, PRIORITIES workspace = os.path.join(os.path.dirname(lulcShp), 'osmtolulc') if not dataStore else dataStore # Check if workspace exists if os.path.exists(workspace): if overwrite: create_folder(workspace) else: raise ValueError('Path {} already exists'.format(workspace)) else: create_folder(workspace) __priorities = PRIORITIES[nomenclature] time_b = datetime.datetime.now().replace(microsecond=0) if RoadsAPI != 'POSTGIS': # ******************************************************************** # # Convert OSM file to SQLITE DB # # ******************************************************************** # osm_db = osm_to_sqdb(osmdata, os.path.join(workspace, 'osm.sqlite')) else: # Convert OSM file to POSTGRESQL DB # conPGSQL["DATABASE"] = create_db(conPGSQL, os.path.splitext( os.path.basename(osmdata))[0], overwrite=True) osm_db = osm_to_pgsql(osmdata, conPGSQL) time_c = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # Add Lulc Classes to OSM_FEATURES by rule # # ************************************************************************ # add_lulc_to_osmfeat(osm_db if RoadsAPI != 'POSTGIS' else conPGSQL, osmTableData, nomenclature, api='SQLITE' if RoadsAPI != 'POSTGIS' else RoadsAPI) time_d = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # Transform SRS of OSM Data # # ************************************************************************ # osmTableData = osm_project( osm_db if RoadsAPI != 'POSTGIS' else conPGSQL, epsg, api='SQLITE' if RoadsAPI != 'POSTGIS' else RoadsAPI) time_e = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # Start a GRASS GIS Session # # ************************************************************************ # grass_base = run_grass(workspace, grassBIN='grass76', location='grloc', srs=epsg) #import grass.script as grass import grass.script.setup as gsetup gsetup.init(grass_base, workspace, 'grloc', 'PERMANENT') # ************************************************************************ # # IMPORT SOME GASP MODULES FOR GRASS GIS # # ************************************************************************ # from gasp.anls.ovlay import erase from gasp.prop.grs import rst_to_region from gasp.mng.genze import dissolve from gasp.mng.grstbl import add_and_update, reset_table from gasp.to.shp.grs import shp_to_grs, grs_to_shp from gasp.to.rst import rst_to_grs # ************************************************************************ # # SET GRASS GIS LOCATION EXTENT # # ************************************************************************ # extRst = rst_to_grs(refRaster, 'extent_raster') rst_to_region(extRst) time_f = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # MapResults # # ************************************************************************ # osmShps = [] # ************************************************************************ # # 1 - Selection Rule # # ************************************************************************ # ruleOneShp, timeCheck1 = grs_vector( osm_db if RoadsAPI != 'POSTGIS' else conPGSQL, osmTableData['polygons'], apidb=RoadsAPI) osmShps.append(ruleOneShp) time_g = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # 2 - Get Information About Roads Location # # ************************************************************************ # ruleRowShp, timeCheck2 = roads_sqdb( osm_db if RoadsAPI == 'SQLITE' else conPGSQL, osmTableData['lines'], osmTableData['polygons'], apidb=RoadsAPI ) if RoadsAPI == 'SQLITE' or RoadsAPI == 'POSTGIS' else grs_vec_roads( osm_db, osmTableData['lines'], osmTableData['polygons']) osmShps.append(ruleRowShp) time_h = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # 3 - Area Upper than # # ************************************************************************ # ruleThreeShp, timeCheck3 = grs_vect_selbyarea( osm_db if RoadsAPI != 'POSTGIS' else conPGSQL, osmTableData['polygons'], UPPER=True, apidb=RoadsAPI) osmShps.append(ruleThreeShp) time_l = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # 4 - Area Lower than # # ************************************************************************ # ruleFourShp, timeCheck4 = grs_vect_selbyarea( osm_db if RoadsAPI != 'POSTGIS' else conPGSQL, osmTableData['polygons'], UPPER=False, apidb=RoadsAPI) osmShps.append(ruleFourShp) time_j = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # 5 - Get data from lines table (railway | waterway) # # ************************************************************************ # ruleFiveShp, timeCheck5 = grs_vect_bbuffer( osm_db if RoadsAPI != 'POSTGIS' else conPGSQL, osmTableData["lines"], api_db=RoadsAPI) osmShps.append(ruleFiveShp) time_m = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # 7 - Assign untagged Buildings to tags # # ************************************************************************ # if nomenclature != "GLOBE_LAND_30": ruleSeven11, ruleSeven12, timeCheck7 = vector_assign_pntags_to_build( osm_db if RoadsAPI != 'POSTGIS' else conPGSQL, osmTableData['points'], osmTableData['polygons'], apidb=RoadsAPI) if ruleSeven11: osmShps.append(ruleSeven11) if ruleSeven12: osmShps.append(ruleSeven12) time_n = datetime.datetime.now().replace(microsecond=0) else: timeCheck7 = None time_n = datetime.datetime.now().replace(microsecond=0) # ************************************************************************ # # Produce LULC Map # # ************************************************************************ # """ Get Shps with all geometries related with one class - One Shape for Classe """ from gasp.mng.gen import same_attr_to_shp _osmShps = [] for i in range(len(osmShps)): if not osmShps[i]: continue _osmShps.append( grs_to_shp(osmShps[i], os.path.join(workspace, osmShps[i] + '.shp'), 'auto', lyrN=1, asCMD=True, asMultiPart=None)) _osmShps = same_attr_to_shp(_osmShps, "cat", workspace, "osm_", resultDict=True) del osmShps time_o = datetime.datetime.now().replace(microsecond=0) """ Merge all Classes into one feature class using a priority rule """ osmShps = {} for cls in _osmShps: if cls == '1': osmShps[1221] = shp_to_grs(_osmShps[cls], "osm_1221", asCMD=True) else: osmShps[int(cls)] = shp_to_grs(_osmShps[cls], "osm_" + cls, asCMD=True) # Erase overlapping areas by priority import copy osmNameRef = copy.deepcopy(osmShps) for e in range(len(__priorities)): if e + 1 == len(__priorities): break if __priorities[e] not in osmShps: continue else: for i in range(e + 1, len(__priorities)): if __priorities[i] not in osmShps: continue else: osmShps[__priorities[i]] = erase( osmShps[__priorities[i]], osmShps[__priorities[e]], "{}_{}".format(osmNameRef[__priorities[i]], e), notTbl=True, api='pygrass') time_p = datetime.datetime.now().replace(microsecond=0) # Export all classes lst_merge = [] for cls in osmShps: if cls == __priorities[0]: reset_table(osmShps[cls], {'cls': 'varchar(5)'}, {'cls': str(cls)}) else: add_and_update(osmShps[cls], {'cls': 'varchar(5)'}, {'cls': str(cls)}) ds = dissolve(osmShps[cls], 'dl_{}'.format(str(cls)), 'cls', api="grass") lst_merge.append( grs_to_shp(ds, os.path.join(workspace, "lulc_{}.shp".format(str(cls))), 'auto', lyrN=1, asCMD=True, asMultiPart=None)) time_q = datetime.datetime.now().replace(microsecond=0) merge_feat(lst_merge, lulcShp, api='pandas') time_r = datetime.datetime.now().replace(microsecond=0) return lulcShp, { 0: ('set_settings', time_b - time_a), 1: ('osm_to_sqdb', time_c - time_b), 2: ('cls_in_sqdb', time_d - time_c), 3: ('proj_data', time_e - time_d), 4: ('set_grass', time_f - time_e), 5: ('rule_1', time_g - time_f, timeCheck1), 6: ('rule_2', time_h - time_g, timeCheck2), 7: ('rule_3', time_l - time_h, timeCheck3), 8: ('rule_4', time_j - time_l, timeCheck4), 9: ('rule_5', time_m - time_j, timeCheck5), 10: None if not timeCheck7 else ('rule_7', time_n - time_m, timeCheck7), 11: ('disj_cls', time_o - time_n), 12: ('priority_rule', time_p - time_o), 13: ('export_cls', time_q - time_p), 14: ('merge_cls', time_r - time_q) }
def optimized_union_anls(lyr_a, lyr_b, outShp, ref_boundary, epsg, workspace=None, multiProcess=None): """ Optimized Union Analysis Goal: optimize v.overlay performance for Union operations """ import os from gasp.oss import get_filename from gasp.mng.sample import create_fishnet from gasp.mng.feat import eachfeat_to_newshp from gasp.mng.gen import merge_feat from gasp.session import run_grass from gasp.anls.exct import split_shp_by_attr if workspace: if not os.path.exists(workspace): from gasp.oss.ops import create_folder create_folder(workspace, overwrite=True) else: from gasp.oss.ops import create_folder workspace = create_folder( os.path.join(os.path.dirname(outShp), "union_work")) # Create Fishnet gridShp = create_fishnet(ref_boundary, os.path.join(workspace, 'ref_grid.shp'), rowN=4, colN=4) # Split Fishnet in several files cellsShp = eachfeat_to_newshp(gridShp, workspace, epsg=epsg) if not multiProcess: # INIT GRASS GIS Session grsbase = run_grass(workspace, location="grs_loc", srs=ref_boundary) import grass.script.setup as gsetup gsetup.init(grsbase, workspace, "grs_loc", 'PERMANENT') # Add data to GRASS GIS from gasp.to.shp.grs import shp_to_grs cellsShp = [ shp_to_grs(shp, get_filename(shp), asCMD=True) for shp in cellsShp ] LYR_A = shp_to_grs(lyr_a, get_filename(lyr_a), asCMD=True) LYR_B = shp_to_grs(lyr_b, get_filename(lyr_b), asCMD=True) # Clip Layers A and B for each CELL in fishnet LYRS_A = [ clip(LYR_A, cellsShp[x], LYR_A + "_" + str(x), api_gis="grass_cmd") for x in range(len(cellsShp)) ] LYRS_B = [ clip(LYR_B, cellsShp[x], LYR_B + "_" + str(x), api_gis="grass_cmd") for x in range(len(cellsShp)) ] # Union SHPS UNION_SHP = [ union(LYRS_A[i], LYRS_B[i], "un_{}".format(i), api_gis="grass_cmd") for i in range(len(cellsShp)) ] # Export Data from gasp.to.shp.grs import grs_to_shp _UNION_SHP = [ grs_to_shp(shp, os.path.join(workspace, shp + ".shp"), "area") for shp in UNION_SHP ] else: def clip_and_union(la, lb, cell, work, ref, proc, output): # Start GRASS GIS Session grsbase = run_grass(work, location="proc_" + str(proc), srs=ref) import grass.script.setup as gsetup gsetup.init(grsbase, work, "proc_" + str(proc), 'PERMANENT') # Import GRASS GIS modules from gasp.to.shp.grs import shp_to_grs from gasp.to.shp.grs import grs_to_shp # Add data to GRASS a = shp_to_grs(la, get_filename(la), asCMD=True) b = shp_to_grs(lb, get_filename(lb), asCMD=True) c = shp_to_grs(cell, get_filename(cell), asCMD=True) # Clip a_clip = clip(a, c, "{}_clip".format(a), api_gis="grass_cmd") b_clip = clip(b, c, "{}_clip".format(b), api_gis="grass_cmd") # Union u_shp = union(a_clip, b_clip, "un_{}".format(c), api_gis="grass_cmd") # Export o = grs_to_shp(u_shp, output, "area") import multiprocessing thrds = [ multiprocessing.Process( target=clip_and_union, name="th-{}".format(i), args=(lyr_a, lyr_b, cellsShp[i], os.path.join(workspace, "th_{}".format(i)), ref_boundary, i, os.path.join(workspace, "uniao_{}.shp".format(i)))) for i in range(len(cellsShp)) ] for t in thrds: t.start() for t in thrds: t.join() _UNION_SHP = [ os.path.join(workspace, "uniao_{}.shp".format(i)) for i in range(len(cellsShp)) ] # Merge all union into the same layer MERGED_SHP = merge_feat(_UNION_SHP, outShp, api="ogr2ogr") return outShp
def distance_between_catpoints(srcShp, facilitiesShp, networkShp, speedLimitCol, onewayCol, grsWorkspace, grsLocation, outputShp): """ Path bet points TODO: Work with files with cat """ import os from gasp.oss import get_filename from gasp.session import run_grass from gasp.mng.gen import merge_feat from gasp.prop.feat import feat_count # Merge Source points and Facilities into the same Feature Class SRC_NFEAT = feat_count(srcShp, gisApi='pandas') FACILITY_NFEAT = feat_count(facilitiesShp, gisApi='pandas') POINTS = merge_feat([srcShp, facilitiesShp], os.path.join(os.path.dirname(outputShp), "points_net.shp"), api='pandas') # Open an GRASS GIS Session gbase = run_grass(grsWorkspace, grassBIN="grass76", location=grsLocation, srs=networkShp) import grass.script as grass import grass.script.setup as gsetup gsetup.init(gbase, grsWorkspace, grsLocation, 'PERMANENT') # Import GRASS GIS Module from gasp.to.shp.grs import shp_to_grs from gasp.to.shp.grs import grs_to_shp from gasp.cpu.grs.mng import category from gasp.mng.grstbl import add_field, add_table, update_table from gasp.mob.grstbx import network_from_arcs from gasp.mob.grstbx import add_pnts_to_network from gasp.mob.grstbx import netpath from gasp.cpu.grs.mng.feat import geomattr_to_db from gasp.cpu.grs.mng.feat import copy_insame_vector # Add Data to GRASS GIS rdvMain = shp_to_grs(networkShp, get_filename(networkShp, forceLower=True)) pntShp = shp_to_grs(POINTS, "points_net") """Get closest facility layer:""" # Connect Points to Network newNetwork = add_pnts_to_network(rdvMain, pntShp, "rdv_points") # Sanitize Network Table and Cost Columns newNetwork = category(newNetwork, "rdv_points_time", "add", LyrN="3", geomType="line") add_table(newNetwork, ("cat integer,kph double precision,length double precision," "ft_minutes double precision," "tf_minutes double precision,oneway text"), lyrN=3) copy_insame_vector(newNetwork, "kph", speedLimitCol, 3, geomType="line") copy_insame_vector(newNetwork, "oneway", onewayCol, 3, geomType="line") geomattr_to_db(newNetwork, "length", "length", "line", createCol=False, unit="meters", lyrN=3) update_table(newNetwork, "kph", "3.6", "kph IS NULL", lyrN=3) update_table(newNetwork, "ft_minutes", "(length * 60) / (kph * 1000.0)", "ft_minutes IS NULL", lyrN=3) update_table(newNetwork, "tf_minutes", "(length * 60) / (kph * 1000.0)", "tf_minutes IS NULL", lyrN=3) # Exagerate Oneway's update_table(newNetwork, "ft_minutes", "1000", "oneway = 'TF'", lyrN=3) update_table(newNetwork, "tf_minutes", "1000", "oneway = 'FT'", lyrN=3) # Produce result result = netpath(newNetwork, "ft_minutes", "tf_minutes", get_filename(outputShp), arcLyr=3, nodeLyr=2) return grs_to_shp(result, outputShp, geomType="line", lyrN=3)