Esempio n. 1
0
def test_utils_py_1():
    """ test get_ovr_idx, create_flat_raster """
    filename = 'tmp/raster.tif'
    temp_files.append(filename)
    raster_creation.create_flat_raster(filename, overview_list=[2, 4])
    ds = util.open_ds(filename)
    compression = util.get_image_structure_metadata(filename, 'COMPRESSION')
    assert compression == 'DEFLATE'
    ovr_count = util.get_ovr_count(ds) + 1
    assert ovr_count == 3
    pixel_size = util.get_pixel_size(ds)
    assert pixel_size == (10, -10)

    for i in range(-ovr_count, ovr_count):
        assert util.get_ovr_idx(filename,
                                ovr_idx=i) == (i if i >= 0 else ovr_count + i)

    for res, ovr in [(5, 0), (10, 0), (11, 0), (19.99, 0), (20, 1), (20.1, 1),
                     (39, 1), (40, 2), (41, 2), (400, 2)]:
        assert util.get_ovr_idx(filename, ovr_res=res) == ovr
        assert util.get_ovr_idx(filename,
                                float(res)) == ovr  # noqa secret functionality

    # test open_ds with multiple different inputs
    filename2 = 'tmp/raster2.tif'
    temp_files.append(filename2)
    raster_creation.create_flat_raster(filename2)
    ds_list = util.open_ds([ds, filename2])
    assert tuple(util.get_ovr_count(ds) for ds in ds_list) == (2, 0)
    ds_list = None
Esempio n. 2
0
def test_utils_py_1():
    """ test get_ovr_idx, create_flat_raster """
    filename = 'tmp/raster.tif'
    temp_files.append(filename)
    raster_creation.create_flat_raster(filename, overview_list=[2, 4])
    ds = util.open_ds(filename)
    compression = util.get_image_structure_metadata(filename, 'COMPRESSION')
    assert compression == 'DEFLATE'
    ras_count = util.get_ovr_count(ds) + 1
    assert ras_count == 3
    pixel_size = util.get_pixel_size(ds)
    assert pixel_size == (10, -10)

    for i in range(-ras_count, ras_count):
        assert util.get_ovr_idx(filename,
                                ovr_idx=i) == (i if i >= 0 else ras_count + i)

    for res, ovr in [(5, 0), (10, 0), (11, 0), (19.99, 0), (20, 1), (20.1, 1),
                     (39, 1), (40, 2), (41, 2), (400, 2)]:
        assert util.get_ovr_idx(filename, ovr_res=res) == ovr
        assert util.get_ovr_idx(
            filename, ovr_idx=float(res)) == ovr  # noqa secret functionality
Esempio n. 3
0
def los_calc(
        vp: MultiPointParams,
        input_filename: Union[gdal.Dataset, PathLikeOrStr, DataSetSelector],
        del_s: float,
        in_coords_srs=None, out_crs=None,
        bi=1, ovr_idx=0, threads=0, of=None,
        backend: ViewshedBackend = None,
        output_filename=None,
        operation: Optional[CalcOperation] = None, color_palette: Optional[ColorPaletteOrPathOrStrings] = None,
        ext_url: Optional[str] = None,
        mock=False):
    input_selector = None
    input_ds = None
    if isinstance(input_filename, PathLikeOrStr.__args__):
        input_ds = gdalos_util.open_ds(input_filename, ovr_idx=ovr_idx)
    elif isinstance(input_filename, DataSetSelector):
        input_selector = input_filename
    else:
        input_ds = input_filename

    srs_4326 = projdef.get_srs(4326)
    pjstr_4326 = srs_4326.ExportToProj4()

    if in_coords_srs is not None:
        in_coords_srs = projdef.get_proj_string(in_coords_srs)

    # figure out in_coords_crs_pj for getting the geo_ox, geo_oy
    if input_selector is None:
        if in_coords_srs is None:
            if input_ds is None:
                input_ds = gdalos_util.open_ds(input_filename, ovr_idx=ovr_idx)
            in_coords_srs = projdef.get_srs_from_ds(input_ds)
    else:
        if in_coords_srs is None:
            in_coords_srs = pjstr_4326

    if isinstance(vp, dict):
        vp = MultiPointParams.get_object_from_lists_dict(vp)
    transform_coords_to_4326 = projdef.get_transform(in_coords_srs, pjstr_4326)
    is_fwd = vp.is_fwd()
    vp.fix_scalars_and_vectors()
    o_points = vp.oxy
    t_points = None if is_fwd else vp.txy
    obs_tar_shape = (len(o_points), 0 if not t_points else len(t_points))
    geo_t = None
    if transform_coords_to_4326:
        # todo: use TransformPoints
        geo_o = transform_coords_to_4326.TransformPoints(o_points)
        if not is_fwd:
            geo_t = transform_coords_to_4326.TransformPoints(t_points)
    else:
        geo_o = o_points
        if not is_fwd:
            geo_t = t_points

    # select the ds
    if input_selector is not None:
        min_x = min_y = math.inf
        max_x = max_y = -math.inf
        for x, y in geo_o:
            if x < min_x:
                min_x = x
            if x > max_x:
                max_x = x
            if y < min_y:
                min_y = y
            if y > max_y:
                max_y = y
        if not is_fwd:
            for x, y in geo_t:
                if x < min_x:
                    min_x = x
                if x > max_x:
                    max_x = x
                if y < min_y:
                    min_y = y
                if y > max_y:
                    max_y = y
        input_filename, input_ds = input_selector.get_item_projected((min_x + max_x) / 2, (min_y + max_y) / 2)
        input_filename = Path(input_filename).resolve()
    if input_ds is None and not mock:
        input_ds = gdalos_util.open_ds(input_filename, ovr_idx=ovr_idx)
        if input_ds is None:
            raise Exception(f'cannot open input file: {input_filename}')

    # figure out the input, output and intermediate srs
    # the intermediate srs will be used for combining the output rasters, if needed
    if input_ds is not None:
        pjstr_input_srs = projdef.get_srs_pj(input_ds)
        pjstr_output_srs = projdef.get_proj_string(out_crs) if out_crs is not None else \
            pjstr_input_srs if input_selector is None else pjstr_4326
        if input_selector is None:
            pjstr_inter_srs = pjstr_input_srs
        else:
            pjstr_inter_srs = pjstr_output_srs

        input_srs = projdef.get_srs_from_ds(input_ds)
        input_raster_is_projected = input_srs.IsProjected()

        is_radio = vp.radio_parameters is not None
        if isinstance(backend, str):
            backend = ViewshedBackend[backend]
        if backend == ViewshedBackend.radio and not is_radio:
            raise Exception('No radio parameters were provided')
        if backend is None or backend == ViewshedBackend.radio:
            backend = default_LOSBackend if (not is_radio or input_raster_is_projected) else \
                ViewshedBackend.z_rest if ext_url else default_RFBackend
        if vp.calc_mode is None:
            raise Exception('calc_mode is None')
        elif not isinstance(vp.calc_mode, (tuple, list)):
            vp.calc_mode = [vp.calc_mode]
        vp.calc_mode = [RadioCalcType[x] if isinstance(x, str) else x for x in vp.calc_mode]

        backend_requires_projected_ds = backend.requires_projected_ds()

        if input_raster_is_projected:
            transform_coords_to_raster = projdef.get_transform(in_coords_srs, pjstr_input_srs)
            projected_filename = input_filename
            if transform_coords_to_raster:
                if is_fwd:
                    vp.ox = np.array(vp.ox, dtype=np.float32)
                    vp.oy = np.array(vp.oy, dtype=np.float32)
                    input_srs = projdef.get_srs(pjstr_input_srs)
                    zone_lon0 = input_srs.GetProjParm('central_meridian')
                    vp.convergence = utm_convergence(vp.ox, vp.oy, zone_lon0)
                else:
                    t_points = transform_coords_to_raster.TransformPoints(t_points)
                o_points = transform_coords_to_raster.TransformPoints(o_points)
        elif backend_requires_projected_ds:
            raise Exception(f'input raster has to be projected')
        else:
            pass
            # projected_pj = get_projected_pj(geo_o[0][0], geo_o[0][1])
            # transform_coords_to_raster = projdef.get_transform(in_coords_srs, projected_pj)
            # vp.ox, vp.oy, _ = transform_coords_to_raster.TransformPoints(vp.ox, vp.oy)
            # d = gdalos_extent.transform_resolution_p(transform_coords_to_raster, 10, 10, vp.ox, vp.oy)
            # extent = GeoRectangle.from_center_and_radius(vp.ox, vp.oy, vp.max_r + d, vp.max_r + d)
            #
            # projected_filename = tempfile.mktemp('.tif')
            # projected_ds = gdalos_trans(
            #     input_ds, out_filename=projected_filename, warp_srs=projected_pj,
            #     extent=extent, return_ds=True, write_info=False, write_spec=False)
            # if not projected_ds:
            #     raise Exception('input raster projection failed')
            # # input_ds = projected_ds

    if not is_fwd:
        o_points, t_points = gdalos_base.make_pairs(o_points, t_points, vp.ot_fill)
    vp.oxy = list(o_points)
    if not is_fwd:
        vp.txy = list(t_points)
    output_names = [x.name for x in vp.calc_mode]
    res = collections.OrderedDict()
    res['backend'] = backend.name
    input_names = ['ox', 'oy', 'oz', 'tx', 'ty', 'tz']

    if backend == ViewshedBackend.rfmodel:
        from rfmodel.rfmodel import calc_path_loss_lonlat_multi
        from tirem.tirem3 import calc_tirem_loss

        inputs = vp.get_as_rfmodel_params(del_s=del_s)
        float_res, bool_res = calc_path_loss_lonlat_multi(calc_tirem_loss, input_ds, **inputs)

        for name in input_names:
            res[name] = getattr(vp, name)
        mode_map = dict(PathLoss=1, FreeSpaceLoss=2)
        for idx, name in enumerate(output_names):
            if name in mode_map:
                res[name] = float_res[mode_map[name]]
            else:
                res[name] = bool_res

    elif backend == ViewshedBackend.z_rest:
        del_s = del_s or get_resolution_meters(input_ds)
        ox, oy, oz = vp.ox, vp.oy, vp.oz
        frequency = vp.radio_parameters.frequency
        polarization = vp.radio_parameters.get_polarization_deg()

        if not isinstance(ox, Sequence):
            ox = [ox]
        if not isinstance(oy, Sequence):
            oy = [oy]
        if not isinstance(oz, Sequence):
            oz = [oz]
        if not isinstance(frequency, Sequence):
            frequency = [frequency]
        if not isinstance(polarization, Sequence):
            polarization = [polarization]

        slices = get_calc_slices(ox, oy, oz)
        k_factor = vp.get_k_factor()

        # res_tx = []
        # res_ty = []
        # res_tz = []
        res_loss = []
        res_los = []
        res_freeloss = []

        for slice in slices:
            data = {
                "kFactor": k_factor,
                "samplingInterval": del_s,
                "originPointWKTGeoWGS84": f"POINT({ox[slice.start]}, {oy[slice.start]})",
                "isfeet1": False,
                "fernelOrder": 0,
                "originAntHeight": oz[slice.start],
                "destPointsRows": [
                    {
                        "destPointWKTGeoWGS84": f"POINT({tx}, {ty})",
                        "destAntHeight": tz,
                        "isfeet": False,
                        "frequencyMhz": f,
                        "polarizationDeg": p,
                        "rowId": idx + 1
                    } for idx, (tx, ty, tz, f, p) in
                    enumerate(zip(vp.tx[slice], vp.ty[slice], vp.tz[slice],
                                  cycle(frequency[slice]), cycle(polarization[slice])))
                ]
            }
            data.update(vp.radio_parameters.as_radiobase_params())
            try:
                response = requests.post(ext_url, json=data)
                res = response.json()
            except Exception as e:
                raise Exception(f'Could not connect to {ext_url} ({e})')
            res = res['operationResult']['pathLossTable']
            res = list_of_dict_to_dict_of_lists(res)

            # res_tx.extend(res['x'])
            # res_ty.extend(res['y'])
            # res_tz.extend(res['height'])

            res_loss.extend(res['medianLoss'])
            res_los.extend(res['isRFLOS'])

            # note this distance is 2d, not taking into account the altitude difference
            dist = calc_dist(ox[slice], oy[slice], vp.tx[slice], vp.ty[slice])
            freeloss = calc_free_space_loss(dist, frequency[slice]).tolist()
            res_freeloss.extend(freeloss)

        res = {  # 'tx': res_tx, 'ty': res_ty, 'tz': res_tz,
            'PathLoss': res_loss, 'FreeSpaceLoss': res_freeloss, 'LOSVisRes': res_los}

    elif backend == ViewshedBackend.talos:
        ovr_idx = get_ovr_idx(projected_filename, ovr_idx)
        if vp.is_fwd():
            vp.calc_fwd(projected_filename, ovr_idx)
        inputs = vp.get_as_talos_params()

        if not mock:
            if not projected_filename:
                raise Exception('to use talos backend you need to provide an input filename')
            from talosgis import talos
            talos_module_init()
            dtm_open_err = talos.GS_DtmOpenDTM(str(projected_filename))
            if dtm_open_err != 0:
                raise Exception(f'talos could not open input file {projected_filename}')
            talos.GS_SetProjectCRSFromActiveDTM()
            talos.GS_DtmSelectOvle(ovr_idx)
            talos.GS_DtmSetCalcThreadsCount(threads or 0)

            talos.GS_SetRefractionCoeff(vp.refraction_coeff)

            if hasattr(talos, 'GS_SetCalcModule'):
                talos.GS_SetCalcModule(vp.get_calc_module())
            radio_params = vp.get_radio_as_talos_params()
            if radio_params:
                talos_radio_init()

                # multi_radio_params = dict_of_reduce_if_same(radio_params)
                # if multi_radio_params:
                #     # raise Exception('unsupported multiple radio parameters')
                #     talos.GS_SetRadioParameters(**radio_params)
                #     result = talos.GS_Radio_Calc(**inputs)
                # else:
                dict_of_selected_items(radio_params, index=0)
                talos.GS_SetRadioParameters(**radio_params)

            result = talos.GS_Radio_Calc(**inputs)
            if result:
                raise Exception('talos calc error')

        float_res = inputs['AIO_re']
        float_res = [float_res[i] for i in range(len(float_res))]

        for name in input_names:
            res[name] = inputs[f'AIO_{name}']

        for idx, name in enumerate(output_names):
            res[name] = float_res[idx]

        if not is_fwd:
            geo_o, geo_t = gdalos_base.make_pairs(geo_o, geo_t, vp.ot_fill)
        vp.oxy = list(geo_o)
        res['ox'] = vp.ox
        res['oy'] = vp.oy
        if not is_fwd:
            vp.txy = list(geo_t)
            res['tx'] = vp.tx
            res['ty'] = vp.ty

        # transform block point from projected to 4326
        transformer = Transformer.from_crs(in_coords_srs, pjstr_input_srs, always_xy=True)
        if any(b in output_names for b in ['bx', 'by']):
            res['bx'], res['by'] = transformer.transform(xx=res['bx'], yy=res['by'],
                                                         direction=TransformDirection.INVERSE)

        if 'LOSVisRes' in output_names and operation:
            res = res['LOSVisRes']
            los = res.reshape(obs_tar_shape)
            res = los_operation(los, operation=operation)
            if color_palette is not None:
                alts = vp.tz  # todo: altitudes need to be absolute (above sea)
                res = poly_to_czml(res, geo_t, alts, color_palette)
    else:
        raise Exception('unknown or unsupported backend {}'.format(backend))

    if res is None:
        raise Exception('error occurred')
    elif output_filename is not None:
        os.makedirs(os.path.dirname(str(output_filename)), exist_ok=True)
        output_filename = Path(output_filename)
        if of != 'xyz':
            res['r'] = [input_filename]
            with open(output_filename, 'w') as outfile:
                json_dump = {k: v.tolist() if isinstance(v, np.ndarray) else str(v) for k, v in res.items()}
                json.dump(json_dump, outfile, indent=2)
        else:
            xyz = np.stack(res.values()).transpose()
            np.savetxt(output_filename, xyz, fmt='%s')

    return res
Esempio n. 4
0
def viewshed_calc_to_ds(
        vp_array,
        input_filename: Union[gdal.Dataset, PathLikeOrStr, DataSetSelector],
        extent=Extent.UNION, cutline=None, calc_cutline: bool = True,
        operation: Optional[CalcOperation] = CalcOperation.count, operation_hidendv=True,
        in_coords_srs=None, out_crs=None,
        color_palette: Optional[ColorPaletteOrPathOrStrings] = None,
        discrete_mode: DiscreteMode = DiscreteMode.interp,
        bi=1, ovr_idx=0, co=None, threads=0,
        vp_slice=None,
        backend: ViewshedBackend = None,
        output_ras: Optional[list] = None,
        temp_files=None,
        files=None):
    input_selector = None
    input_ds = None
    calc_cutline = None if not calc_cutline else cutline if isinstance(calc_cutline, bool) else calc_cutline
    if isinstance(input_filename, PathLikeOrStr.__args__):
        input_ds = gdalos_util.open_ds(input_filename, ovr_idx=ovr_idx)
    elif isinstance(input_filename, DataSetSelector):
        input_selector = input_filename
    else:
        input_ds = input_filename

    if isinstance(extent, int):
        extent = Extent(extent)
    elif isinstance(extent, str):
        extent = Extent[extent]

    is_temp_file, gdal_out_format, d_path, return_ds = temp_params(False)

    color_palette = gdalos_color.get_color_palette(color_palette)
    color_table = None
    if operation == CalcOperation.viewshed:
        operation = None

    do_post_color = False
    temp_files = temp_files if temp_files is not None else []

    vp_slice = make_slice(vp_slice)

    if isinstance(discrete_mode, str):
        discrete_mode = DiscreteMode[discrete_mode]

    if not files:
        files = []
    else:
        files = files.copy()[vp_slice]

    if not files:
        if isinstance(vp_array, ViewshedParams):
            vp_array = [vp_array]
        else:
            if isinstance(vp_array, dict):
                vp_array = ViewshedParams.get_list_from_lists_dict(vp_array)
            vp_array = vp_array[vp_slice]

        if operation:
            # restore viewshed consts default values
            my_viewshed_defaults = viewshed_params.viewshed_defaults
            for a in vp_array:
                a.update(my_viewshed_defaults)
        else:
            vp_array = vp_array[0:1]

        max_rasters_count = 1 if operation is None else 254 if operation == CalcOperation.unique else 1000
        if len(vp_array) > max_rasters_count:
            vp_array = vp_array[0:max_rasters_count]

        srs_4326 = projdef.get_srs(4326)
        pjstr_4326 = srs_4326.ExportToProj4()

        first_vs = True

        if in_coords_srs is not None:
            in_coords_srs = projdef.get_proj_string(in_coords_srs)

        for vp in vp_array:
            # vp might get changed, so make a copy
            vp = copy.copy(vp)
            if first_vs or input_selector is not None:
                first_vs = False
                # figure out in_coords_crs_pj for getting the geo_ox, geo_oy
                if input_selector is None:
                    if in_coords_srs is None:
                        if input_ds is None:
                            input_ds = gdalos_util.open_ds(input_filename, ovr_idx=ovr_idx)
                        in_coords_srs = projdef.get_srs_from_ds(input_ds)
                else:
                    if in_coords_srs is None:
                        in_coords_srs = pjstr_4326

                transform_coords_to_4326 = projdef.get_transform(in_coords_srs, pjstr_4326)
                if transform_coords_to_4326:
                    geo_ox, geo_oy, _ = transform_coords_to_4326.TransformPoint(vp.ox, vp.oy)
                else:
                    geo_ox, geo_oy = vp.ox, vp.oy

                # select the ds
                if input_selector is not None:
                    input_filename, input_ds = input_selector.get_item_projected(geo_ox, geo_oy)
                    input_filename = Path(input_filename).resolve()
                if input_ds is None:
                    input_ds = gdalos_util.open_ds(input_filename, ovr_idx=ovr_idx)
                    if input_ds is None:
                        raise Exception(f'cannot open input file: {input_filename}')

                # figure out the input, output and intermediate srs
                # the intermediate srs will be used for combining the output rasters, if needed
                pjstr_input_srs = projdef.get_srs_pj(input_ds)
                pjstr_output_srs = projdef.get_proj_string(out_crs) if out_crs is not None else \
                    pjstr_input_srs if input_selector is None else pjstr_4326
                if input_selector is None:
                    pjstr_inter_srs = pjstr_input_srs
                else:
                    pjstr_inter_srs = pjstr_output_srs

                input_srs = projdef.get_srs_from_ds(input_ds)
                input_raster_is_projected = input_srs.IsProjected()
                if input_raster_is_projected:
                    transform_coords_to_raster = projdef.get_transform(in_coords_srs, pjstr_input_srs)
                else:
                    raise Exception(f'input raster has to be projected')
            zone_lon0 = input_srs.GetProjParm('central_meridian')
            vp.convergence = utm_convergence(vp.ox, vp.oy, zone_lon0)
            if input_raster_is_projected:
                projected_filename = input_filename
                if transform_coords_to_raster:
                    vp.ox, vp.oy, _ = transform_coords_to_raster.TransformPoint(vp.ox, vp.oy)
            else:
                projected_pj = get_projected_pj(geo_ox, geo_oy)
                transform_coords_to_raster = projdef.get_transform(in_coords_srs, projected_pj)
                vp.ox, vp.oy, _ = transform_coords_to_raster.TransformPoint(vp.ox, vp.oy)
                d = gdalos_extent.transform_resolution_p(transform_coords_to_raster, 10, 10, vp.ox, vp.oy)
                extent = GeoRectangle.from_center_and_radius(vp.ox, vp.oy, vp.max_r + d, vp.max_r + d)

                projected_filename = tempfile.mktemp('.tif')
                projected_ds = gdalos_trans(
                    input_ds, out_filename=projected_filename, warp_srs=projected_pj,
                    extent=extent, return_ds=True, write_info=False, write_spec=False)
                if not projected_ds:
                    raise Exception('input raster projection failed')
                input_ds = projected_ds

            if backend is None:
                backend = default_ViewshedBackend
            elif isinstance(backend, str):
                backend = ViewshedBackend[backend]
            if backend == ViewshedBackend.radio:
                backend = ViewshedBackend.talos

            is_base_calc = True
            if backend == ViewshedBackend.gdal:
                # TypeError: '>' not supported between instances of 'NoneType' and 'int'
                bnd_type = gdal.GDT_Byte
                # todo: why dosn't it work without it?
                is_temp_file, gdal_out_format, d_path, return_ds = temp_params(True)

                inputs = vp.get_as_gdal_params()
                print(inputs)

                input_band: Optional[gdal.Band] = input_ds.GetRasterBand(bi)
                if input_band is None:
                    raise Exception('band number out of range')
                ds = gdal.ViewshedGenerate(input_band, gdal_out_format, str(d_path), co, **inputs)
                input_band = None  # close band

                if not ds:
                    raise Exception('Viewshed calculation failed')
            elif backend == ViewshedBackend.talos:
                # is_temp_file = True  # output is file, not ds
                if not projected_filename:
                    raise Exception('to use talos backend you need to provide an input filename')

                from talosgis import talos
                talosgis_version = talos_module_init()
                dtm_open_err = talos.GS_DtmOpenDTM(str(projected_filename))
                talos.GS_SetProjectCRSFromActiveDTM()
                ovr_idx = get_ovr_idx(projected_filename, ovr_idx)
                talos.GS_DtmSelectOvle(ovr_idx)
                talos.GS_DtmSetCalcThreadsCount(threads or 0)
                if dtm_open_err != 0:
                    raise Exception('talos could not open input file {}'.format(projected_filename))
                talos.GS_SetRefractionCoeff(vp.refraction_coeff)

                inputs = vp.get_as_talos_params()
                bnd_type = inputs['result_dt']
                is_base_calc = bnd_type in [gdal.GDT_Byte]
                inputs['low_nodata'] = is_base_calc or operation == CalcOperation.max
                if hasattr(talos, 'GS_SetCalcModule'):
                    module = vp.get_calc_module()
                    talos.GS_SetCalcModule(module)
                if vp.is_radio():
                    talos_radio_init()
                    radio_params = vp.get_radio_as_talos_params(0)
                    talos.GS_SetRadioParameters(**radio_params)
                talos.GS_SetInterestAreaCalcMethod(
                    CalcOnlyInInterestArea=bool(calc_cutline), ClearOutsideInterestArea=False)
                X0Pixel = Y0Pixel = ras = h_ras = e_ras = a_ras = r_ras = None
                if talosgis_version >= (3, 6):
                    if calc_cutline:
                        vertex_count, xys = polygon_to_np(calc_cutline)
                        talos.GS_SetInterestArea1(vertex_count, xys, False)
                    result = talos.GS_Viewshed_Calc(**inputs, CheckNonVoid=False)
                    _unexpected, X0Pixel, Y0Pixel, ras, h_ras, e_ras, a_ras, r_ras = result
                elif 'GS_Viewshed_Calc2' in dir(talos):
                    ras = talos.GS_Viewshed_Calc2(**inputs)
                else:
                    del inputs['out_res']
                    ras = talos.GS_Viewshed_Calc1(**inputs)

                if ras is None:
                    raise Exception(f'fail to calc viewshed: {inputs}')

                do_post_color = color_palette and (bnd_type not in [gdal.GDT_Byte, gdal.GDT_UInt16])

                ras_map = {
                    'v': ras,  # visibility
                    'h': h_ras,  # heights / dtm
                    'r': r_ras,  # ranges
                    'a': a_ras,  # azimuths
                    'e': e_ras,  # elevations
                }
                output_ras = output_ras or ['v']
                output_ras = [s[0].lower() for s in output_ras]
                my_rasters = [v for k, v in ras_map.items() if k in output_ras and v is not None]
                my_ds = []
                for r in my_rasters:
                    # talos supports only file output (not ds)
                    is_temp_file, gdal_out_format, d_path, return_ds = temp_params(True)
                    temp_files.append(d_path)
                    talos.GS_SaveRaster(r, str(d_path))
                    # I will reopen the ds to change the color table and ndv
                    # ds = gdalos_util.open_ds(d_path, access_mode=gdal.OF_UPDATE)
                    ds: gdal.Dataset = gdal.OpenEx(str(d_path), gdal.OF_RASTER | gdal.OF_UPDATE)
                    my_ds.append(ds)
                if len(my_ds) > 1:
                    d_path = str(d_path) + '_vrt.vrt'
                    # let's stack all these bands into a single vrt
                    ds = gdal.BuildVRT(d_path, my_ds, separate=True)
                    temp_files.append(d_path)
                my_ds = None
            else:
                raise Exception('unknown backend {}'.format(backend))

            input_ds = None

            set_nodata = backend == ViewshedBackend.gdal
            # set_nodata = is_base_calc
            bnd = ds.GetRasterBand(1)
            if set_nodata:
                base_calc_ndv = vp.ndv
                bnd.SetNoDataValue(base_calc_ndv)
            else:
                base_calc_ndv = bnd.GetNoDataValue()
            if color_palette and not do_post_color:
                if bnd_type != bnd.DataType:
                    raise Exception('Unexpected band type, expected: {}, got {}'.format(bnd_type, bnd.DataType))
                if not color_palette.is_numeric():
                    min_max = bnd.ComputeRasterMinMax()
                    color_palette.apply_percent(*min_max)
                color_table = gdalos_color.get_color_table(color_palette)
                if color_table is None:
                    raise Exception('Could not create color table')
                bnd.SetRasterColorTable(color_table)
                bnd.SetRasterColorInterpretation(gdal.GCI_PaletteIndex)
            bnd = None

            # if is_temp_file:
            #     # close original ds and reopen
            #     ds = None
            #     ds = gdalos_util.open_ds(d_path)

            cut_sector = (backend == ViewshedBackend.gdal) and not vp.is_omni_h()
            # warp_result = False
            warp_result = (input_selector is not None)
            if warp_result or cut_sector:
                if cut_sector:
                    ring = PolygonizeSector(vp.ox, vp.oy, vp.max_r, vp.max_r, vp.get_grid_azimuth(), vp.h_aperture)
                    post_calc_cutline = tempfile.mktemp(suffix='.gpkg')
                    temp_files.append(post_calc_cutline)
                    create_layer_from_geometries([ring], post_calc_cutline)
                else:
                    post_calc_cutline = None
                # todo: check why without temp file it crashes on operation
                is_temp_file, gdal_out_format, d_path, return_ds = temp_params(True)
                scale = ds.GetRasterBand(1).GetScale()
                ds = gdalos_trans(ds, out_filename=d_path, warp_srs=pjstr_inter_srs,
                                  cutline=post_calc_cutline, of=gdal_out_format, return_ds=return_ds,
                                  ovr_type=OvrType.no_overviews)
                if is_temp_file:
                    # close original ds and reopen
                    ds = None
                    ds = gdalos_util.open_ds(d_path)
                    if scale and workaround_warp_scale_bug:
                        ds.GetRasterBand(1).SetScale(scale)
                    temp_files.append(d_path)
                if not ds:
                    raise Exception('Viewshed calculation failed to cut')

            if operation:
                files.append(ds)

    if operation:
        # alpha_pattern = '1*({{}}>{})'.format(viewshed_thresh)
        # alpha_pattern = 'np.multiply({{}}>{}, dtype=np.uint8)'.format(viewshed_thresh)
        no_data_value = base_calc_ndv
        if operation == CalcOperation.viewshed:
            # no_data_value = viewshed_params.viewshed_ndv
            f = gdalos_combine.get_by_index
            # calc_expr, calc_kwargs, f = gdal_calc.make_calc_with_func(files, alpha_pattern, 'f'), sum
        elif operation == CalcOperation.max:
            # no_data_value = viewshed_params.viewshed_ndv
            f = gdalos_combine.vs_max
            # calc_expr, calc_kwargs, f = gdal_calc.make_calc_with_func(files, alpha_pattern, 'f'), sum
        elif operation == CalcOperation.min:
            f = gdalos_combine.vs_min
        elif operation == CalcOperation.count:
            no_data_value = 0
            f = gdalos_combine.vs_count
            # calc_expr, calc_kwargs = gdal_calc.make_calc_with_operand(files, alpha_pattern, '+')
            # calc_expr, calc_kwargs, f = gdal_calc.make_calc_with_func(files, alpha_pattern), sum
        elif operation == CalcOperation.count_z:
            no_data_value = viewshed_params.viewshed_comb_ndv
            f = partial(gdalos_combine.vs_count_z, in_ndv=base_calc_ndv)
            # calc_expr, calc_kwargs f, = gdal_calc.make_calc_with_func(files, alpha_pattern, 'f'), sum
        elif operation == CalcOperation.unique:
            no_data_value = viewshed_params.viewshed_comb_ndv
            f = gdalos_combine.vs_unique
            # calc_expr, calc_kwargs, f = gdal_calc.make_calc_with_func(files, alpha_pattern, 'f'), unique
        else:
            raise Exception('Unknown operation: {}'.format(operation))

        calc_expr = 'f(x)'
        calc_kwargs = dict(x=files)
        user_namespace = dict(f=f)

        debug_time = 1
        t = time.time()
        for i in range(debug_time):
            is_temp_file, gdal_out_format, d_path, return_ds = temp_params(False)
            ds = gdal_calc.Calc(
                calc_expr, outfile=str(d_path), extent=extent, format=gdal_out_format,
                color_table=color_table, overwrite=True,
                NoDataValue=no_data_value, hideNoData=operation_hidendv,
                user_namespace=user_namespace, **calc_kwargs)
        t = time.time() - t
        print('time for calc: {:.3f} seconds'.format(t))

        if not ds:
            raise Exception('error occurred')
        for i in range(len(files)):
            files[i] = None  # close calc input ds(s)

    combined_post_process_needed = cutline or not projdef.are_srs_equivalent(pjstr_inter_srs, pjstr_output_srs)
    if combined_post_process_needed:
        is_temp_file, gdal_out_format, d_path, return_ds = temp_params(False)
        ds = gdalos_trans(ds, out_filename=d_path, warp_srs=pjstr_output_srs,
                          cutline=cutline, of=gdal_out_format, return_ds=return_ds, ovr_type=OvrType.no_overviews)

        if return_ds:
            if not ds:
                raise Exception('error occurred')

    if do_post_color:
        is_temp_file, gdal_out_format, d_path, return_ds = temp_params(False)
        ds = gdalos_raster_color(ds, out_filename=d_path, color_palette=color_palette, discrete_mode=discrete_mode)
        if not ds:
            raise Exception('Viewshed calculation failed to color result')

    removed = []
    if temp_files:
        for f in temp_files:
            try:
                os.remove(f)
                removed.append(f)
            except:
                pass
                # probably this is a file that backs the ds that we'll return
                # print('failed to remove temp file:{}'.format(f))
    for f in removed:
        temp_files.remove(f)
    return ds