# 设备所在业务空间
## 前置条件
    1. 业务空间有所属楼层关系
    2. 设备有所属楼层关系
    3. 业务空间有轮廓数据(outline信息点)
    4. 设备有坐标信息(bimLocation信息点)
## 依赖函数
    Eq2Fl, Sp2Fl
## 处理逻辑
    1. 取出项目内的所有业务空间和设备, 将在同一楼层内的设备和业务空间分成一组
    2. 在一组内, 判断设备的bim_location是否在业务空间的outline内, 即做点在多边形内的判断, 点在多边形内则认为有关系

# 函数
## 代码

<details>
<summary>源码</summary>

```
CREATE OR REPLACE FUNCTION "public"."rel_eq2sp"("tables" text, "out_tables" text, "project_id" varchar, "sign1" int4, "sign2" int4)
  RETURNS "pg_catalog"."bool" AS $BODY$
from matplotlib.path import Path
import json

def is_in_meta_polygon(point, single_poly, radius):
    poly_len = len(single_poly)
    poly = []
    for i in range(poly_len):
        pair = single_poly[i]
        poly.append((pair["X"], pair["Y"]))
    p = Path(poly)
    return p.contains_points([point], None, radius)


def is_in_polygon(point, polygons):
    polygons_length = len(polygons)
    if polygons_length == 0:
        return False
    for j in range(polygons_length):
        polygon = polygons[j]
        if j == 0:
            if not is_in_meta_polygon(point, polygon, -0.001):
                return False
        else:
            if is_in_meta_polygon(point, polygon, 0.001):
                return False
    return True


def is_point_in_polygon(x, y, json_poly):
    try:
        polygon_list = json.loads(json_poly)
        total_len = len(polygon_list)
        point_pair = (float(x), float(y))
        for index in range(total_len):
            if is_in_polygon(point_pair, polygon_list[index]):
                return True
        return False
    except Exception as e:
        plpy.info(e)
        return False


# 将下面对数据库的操作作为一个事务, 出异常则自动rollback
input_table_list = tables.split(',')
output_table_list = out_tables.split(',')
with plpy.subtransaction():
    for i in range(0, len(input_table_list)):
        in_table_name = input_table_list[i]
        out_table_name = output_table_list[i]
        # 删除原来关系表中的数据
        plan1 = plpy.prepare("delete from {0} where project_id = $1 and (sign = $2 or sign = $3)".format(out_table_name.strip()), ["text", "integer", "integer"])
        plan1.execute([project_id, sign1, sign2])
        # 计算关系
        plan_floor = plpy.prepare("select id from floor where project_id = $1", ["text"])
        floors = plan_floor.execute([project_id])
        # 按楼层计算
        for floor in floors:
            floor_id = floor['id']
            # 获取楼层下的设备
            plan_equip = plpy.prepare("select id, bim_location from equipment where project_id = $1 and bim_location is not null and floor_id = $2", ["text", "text"])
            equips = plan_equip.execute([project_id, floor_id])
            if len(equips) == 0:
                continue
            # 获取楼层下的业务空间

            space_plan = plpy.prepare("select sp.id, sp.outline, sp.object_type from public.r_sp_in_fl rel inner join {0} sp on rel.space_id = sp.id where outline is not null and rel.floor_id = $1".format(in_table_name), ["text"])
            spaces = space_plan.execute([floor_id])
            if len(spaces) == 0:
                continue
            # 判断设备的bim_location是否在业务空间的outline内
            for equip in equips:
                for space in spaces:
                    try:
                        location = equip['bim_location'].split(',')
                        if is_point_in_polygon(location[0], location[1], space['outline']):
                            # 设备在业务空间内, 添加关系
                            insert_plan = plpy.prepare("insert into {0}(eq_id, sp_id, project_id, sign, type, zone_type) values($1, $2, $3, 2, 'eq2sp_in', $4)".format(out_table_name.strip()), ["text", "text", "text", "text"])
                            insert_plan.execute([equip['id'], space['id'], project_id, space['object_type']])
                    except Exception as ex:
                        plpy.warning(ex)
                        continue
return True
$BODY$
  LANGUAGE plpython3u VOLATILE
  COST 100


例子:
select public.rel_eq2sp('zone_general,zone_lighting', 'r_eq_in_sp_zone_general,r_eq_in_sp_zone_lighting', 'Pj1101010015', 2, 2);
```
</details>


## 输入
    1. 参与计算的业务空间表的全名称, 带schema名, 以英文逗号隔开
    2. 关系计算结果存储的表, 跟第一个参数一一对应, 带schema名, 以英文逗号隔开
    3. 项目id
    4. 要被删除的sign, 只能是int型, 值为1或2 (1表示手动维护的关系, 2表示自动计算的关系)
    5. 第二个要被删除的sign, 只能是int型, 值为1或2 (1表示手动维护的关系, 2表示自动计算的关系)
## 返回结果
    true    成功
    false   失败
