设备所在楼层
## 前置条件
    1. 设备有ModelId信息点  (模型web服务的模型id)
    2. 楼层有ModelId信息点  (模型web服务的楼层id)
    3. 设备没有所在楼层关系
    4. 设备有Location(非必要)
    5. 楼层有Outline(非必要)
    

## 处理逻辑
    1. 如果楼层有Outline, 设备有Location, 做点(Location)在多边形(Outline)内的判断, 点在多边形内则认为有关系
    2. 如果不满足条件1, 即楼层没有Outline, 设备没有Location, 则判断它们ModelId信息点的值, 相等则认为有关系   
        (此处的相等表示设备上的ModelId信息点在关系上从属于楼层上的ModelId信息点, 即模型web服务中的模型id属于楼层id)

## 技术细节
    1. 做点在多边形内的判断, 使用到了python的 shapely 库 中的 Path 类的contains_points 方法

# 函数

## 代码
<details>
<summary>源码</summary>

```
-- 设备所在楼层
CREATE OR REPLACE FUNCTION "public"."rel_eq2fl"("project_id" varchar)
  RETURNS "pg_catalog"."bool" AS $BODY$
from matplotlib.path import Path
import json

def is_in_meta_polygon(x, y, json_poly):
    try:
        poly = []
        polygon = json.loads(json_poly)
        l = len(polygon)
        points = (float(x), float(y))
        for i in range(l):
            pair = polygon[i]
            poly.append((pair["X"], pair["Y"]))

        p = Path(poly)
    except Exception as e:
        plpy.info(e)
        return False
    else:
        return p.contains_points([points], None, -0.0001)


model_floor_map = dict()

def get_model_floor(model_id):
    try:
        if model_id in model_floor_map :
            return model_floor_map[model_id]
        model_floor_plan = plpy.prepare("select model_floor_id from revit.model_file where id = $1", ["text"])
        model_floor_arr = model_floor_plan.execute([model_id])
        if len(model_floor_arr) == 0:
            return None
        model_floor = model_floor_arr[0].get('model_floor_id')
        model_floor_map[model_id] = model_floor
        return model_floor
    except Exception as e:
        return None

try:
    # 将下面对数据库的操作作为一个事务, 出异常则自动rollback
    with plpy.subtransaction():
        floor_plan = plpy.prepare("select id, outline, model_id from floor where project_id = $1 and model_id is not null", ["text"])
        floors = floor_plan.execute([project_id])
        equip_plan = plpy.prepare("select id, bim_location, model_id from equipment where project_id = $1 and model_id is not null", ["text"])
        equips = equip_plan.execute([project_id])
        for floor in floors:
            for equip in equips:
                outline = floor['outline']
                location = equip['bim_location']
                if outline == None or len(outline) == 0 or location == None or len(location) == 0:
                    model_floor_id = get_model_floor(equip['model_id'])
                    if model_floor_id is None:
                        continue
                    if floor['model_id'] == model_floor_id:
                        equip['floor_id'] = floor['id']
                    continue
                location = location.split(',')
                try:
                    if floor['model_id'] == model_floor_id:
                        if is_in_meta_polygon(location[0], location[1], outline):
                            equip['floor_id'] = floor['id']
                except Exception as ex:
                    continue
        for equip in equips:
            if equip.__contains__('floor_id'):
                try:
                    #plpy.info("equip id {0}, floor id {1}".format(equip['id'], equip['floor_id']))
                    plan = plpy.prepare("update equipment set floor_id = $1 where id = $2", ["text", "text"])
                    plan.execute([equip['floor_id'], equip['id']])
                except Exception as ex:
                    continue
except Exception as e:
    plpy.warning(e)
    return False
else:
    return True
$BODY$
  LANGUAGE plpython3u VOLATILE
  COST 100


select public.rel_eq2fl('Pj1101010015');
```
</details>

## 输入
    1. 项目id
## 返回结果
    true    成功
    false   失败
