diff --git a/api/__init__.py b/api/__init__.py index b621991..95cc492 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -148,9 +148,13 @@ from .s33_dma import get_all_district_metering_area_ids, get_all_district_meteri from .s33_dma_gen import generate_district_metering_area, generate_sub_district_metering_area from .s34_sa_cal import calculate_service_area -from .s34_sa import get_service_area_schema, get_service_area, set_service_area, add_service_area, delete_service_area, get_all_service_area_ids, get_all_service_areas +from .s34_sa import get_service_area_schema, get_service_area, set_service_area, add_service_area, delete_service_area +from .s34_sa import get_all_service_area_ids, get_all_service_areas from .s34_sa_gen import generate_service_area from .s35_vd_cal import calculate_virtual_district +from .s35_vd import get_virtual_district_schema, get_virtual_district, set_virtual_district, add_virtual_district, delete_virtual_district +from .s35_vd import get_all_virtual_district_ids, get_all_virtual_districts +from .s35_vd_gen import generate_virtual_district from .s36_wda_cal import calculate_demand_to_nodes, calculate_demand_to_region, calculate_demand_to_network diff --git a/api/batch_exe.py b/api/batch_exe.py index 7675d7b..080688e 100644 --- a/api/batch_exe.py +++ b/api/batch_exe.py @@ -33,6 +33,7 @@ from .s31_scada_element import set_scada_element, add_scada_element, delete_scad from .s32_region import set_region, add_region, delete_region from .s33_dma import set_district_metering_area, add_district_metering_area, delete_district_metering_area from .s34_sa import set_service_area, add_service_area, delete_service_area +from .s35_vd import set_virtual_district, add_virtual_district, delete_virtual_district from .batch_api_cs import rewrite_batch_api @@ -115,6 +116,8 @@ def _execute_add_command(name: str, cs: ChangeSet) -> ChangeSet: return add_district_metering_area(name, cs) elif type == s34_sa: return add_service_area(name, cs) + elif type == s35_vd: + return add_virtual_district(name, cs) return ChangeSet() @@ -200,6 +203,8 @@ def _execute_update_command(name: str, cs: ChangeSet) -> ChangeSet: return set_district_metering_area(name, cs) elif type == s34_sa: return set_service_area(name, cs) + elif type == s35_vd: + return set_virtual_district(name, cs) return ChangeSet() @@ -283,6 +288,8 @@ def _execute_delete_command(name: str, cs: ChangeSet) -> ChangeSet: return delete_district_metering_area(name, cs) elif type == s34_sa: return delete_service_area(name, cs) + elif type == s35_vd: + return delete_virtual_district(name, cs) return ChangeSet() diff --git a/api/s35_vd.py b/api/s35_vd.py new file mode 100644 index 0000000..feddd1d --- /dev/null +++ b/api/s35_vd.py @@ -0,0 +1,202 @@ +from .database import * +from .s0_base import is_node +from .s32_region_util import to_postgis_polygon +from .s32_region import get_region + +def get_virtual_district_schema(name: str) -> dict[str, dict[str, Any]]: + return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True }, + 'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False }, + 'center' : {'type': 'str' , 'optional': False , 'readonly': False } } + +def get_virtual_district(name: str, id: str) -> dict[str, Any]: + vd = get_region(name, id) + if vd == {}: + return {} + r = try_read(name, f"select * from region_vd where id = '{id}'") + if r == None: + return {} + vd['center'] = r['center'] + vd['nodes'] = list(eval(r['nodes'])) + return vd + +def _set_virtual_district(name: str, cs: ChangeSet) -> DbChangeSet: + id = cs.operations[0]['id'] + + new_boundary = cs.operations[0]['boundary'] + old_boundary = get_region(name, id)['boundary'] + + new_center = cs.operations[0]['center'] + f_new_center = f"'{new_center}'" + + new_nodes = cs.operations[0]['nodes'] + str_new_nodes = str(new_nodes).replace("'", "''") + + old = get_virtual_district(name, id) + old_center = old['center'] + f_old_center = f"'{old_center}'" + + old_nodes = old['nodes'] + str_old_nodes = str(old_nodes).replace("'", "''") + + redo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(new_boundary)}') where id = '{id}';" + redo_sql += f"update region_vd set center = {f_new_center}, nodes = '{str_new_nodes}' where id = '{id}';" + + undo_sql = f"update region_vd set center = {f_old_center}, nodes = '{str_old_nodes}' where id = '{id}';" + undo_sql += f"update region set boundary = st_geomfromtext('{to_postgis_polygon(old_boundary)}') where id = '{id}';" + + redo_cs = g_update_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': new_boundary, 'center': new_center, 'nodes': new_nodes } + undo_cs = g_update_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': old_boundary, 'center': old_center, 'nodes': old_nodes } + + return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs]) + + +def set_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: + ops = cs.operations + + if len(cs.operations) == 0: + return ChangeSet() + + op = ops[0] + + if 'id' not in op: + return ChangeSet() + + vd = get_virtual_district(name, op['id']) + if vd == {}: + return ChangeSet() + + if 'boundary' not in op: + op['boundary'] = vd['boundary'] + else: + b = op['boundary'] + if len(b) < 4 or b[0] != b[-1]: + return ChangeSet() + + if 'center' not in op: + op['center'] = vd['center'] + + if not is_node(name, op['center']): + return ChangeSet() + + if 'nodes' not in op: + op['nodes'] = vd['nodes'] + else: + for node in op['nodes']: + if not is_node(name, node): + return ChangeSet() + + return execute_command(name, _set_virtual_district(name, cs)) + + +def _add_virtual_district(name: str, cs: ChangeSet) -> DbChangeSet: + id = cs.operations[0]['id'] + + boundary = cs.operations[0]['boundary'] + + center = cs.operations[0]['center'] + f_center = f"'{center}'" + + nodes = cs.operations[0]['nodes'] + str_nodes = str(nodes).replace("'", "''") + + redo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'VD');" + redo_sql += f"insert into region_vd (id, center, nodes) values ('{id}', {f_center}, '{str_nodes}');" + + undo_sql = f"delete from region_vd where id = '{id}';" + undo_sql += f"delete from region where id = '{id}';" + + redo_cs = g_add_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': boundary, 'center': center, 'nodes': nodes } + undo_cs = g_delete_prefix | { 'type': 'virtual_district', 'id': id } + + return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs]) + + +def add_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: + ops = cs.operations + + if len(cs.operations) == 0: + return ChangeSet() + + op = ops[0] + + if 'id' not in op: + return ChangeSet() + + vd = get_virtual_district(name, op['id']) + if vd != {}: + return ChangeSet() + + if 'boundary' not in op: + return ChangeSet() + else: + b = op['boundary'] + if len(b) < 4 or b[0] != b[-1]: + return ChangeSet() + + if 'center' not in op: + return ChangeSet() + + if not is_node(name, op['center']): + return ChangeSet() + + if 'nodes' not in op: + op['nodes'] = [] + else: + for node in op['nodes']: + if not is_node(name, node): + return ChangeSet() + + return execute_command(name, _add_virtual_district(name, cs)) + + +def _delete_virtual_district(name: str, cs: ChangeSet) -> DbChangeSet: + id = cs.operations[0]['id'] + vd = get_virtual_district(name, id) + boundary = vd['boundary'] + center = vd['center'] + f_center = f"'{center}'" + nodes = vd['nodes'] + str_nodes = str(nodes).replace("'", "''") + + redo_sql = f"delete from region_vd where id = '{id}';" + redo_sql += f"delete from region where id = '{id}';" + + undo_sql = f"insert into region (id, boundary, r_type) values ('{id}', '{to_postgis_polygon(boundary)}', 'VD');" + undo_sql += f"insert into region_vd (id, center, nodes) values ('{id}', {f_center}, '{str_nodes}');" + + redo_cs = g_delete_prefix | { 'type': 'virtual_district', 'id': id } + undo_cs = g_add_prefix | { 'type': 'virtual_district', 'id': id, 'boundary': boundary, 'center': center, 'nodes': nodes } + + return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs]) + + +def delete_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: + ops = cs.operations + + if len(cs.operations) == 0: + return ChangeSet() + + op = ops[0] + + if 'id' not in op: + return ChangeSet() + + vd = get_virtual_district(name, op['id']) + if vd == {}: + return ChangeSet() + + return execute_command(name, _delete_virtual_district(name, cs)) + + +def get_all_virtual_district_ids(name: str) -> list[str]: + ids = [] + for row in read_all(name, f"select id from region_vd"): + ids.append(row['id']) + return ids + + +def get_all_virtual_districts(name: str) -> list[dict[str, Any]]: + result = [] + for id in get_all_virtual_district_ids(name): + result.append(get_virtual_district(name, id)) + return result diff --git a/api/s35_vd_gen.py b/api/s35_vd_gen.py new file mode 100644 index 0000000..639ffd1 --- /dev/null +++ b/api/s35_vd_gen.py @@ -0,0 +1,21 @@ +from .s32_region_util import calculate_boundary, inflate_boundary +from .s35_vd_cal import * +from .s35_vd import get_all_virtual_district_ids +from .batch_exe import execute_batch_command + +def generate_virtual_district(name: str, centers: list[str]) -> ChangeSet: + cs = ChangeSet() + + for id in get_all_virtual_district_ids(name): + cs.delete({'type': 'virtual_district', 'id': id}) + + vds = calculate_virtual_district(name, centers)['virtual_districts'] + + for vd in vds: + center = vd['center'] + nodes = vd['nodes'] + boundary = calculate_boundary(name, nodes) + boundary = inflate_boundary(name, boundary) + cs.add({ 'type': 'virtual_district', 'id': f"VD_{center}", 'boundary': boundary, 'center': center, 'nodes': nodes }) + + return execute_batch_command(name, cs) diff --git a/api/sections.py b/api/sections.py index 130e882..7cd74f6 100644 --- a/api/sections.py +++ b/api/sections.py @@ -36,6 +36,7 @@ s31_scada_element = 'scada_element' s32_region = 'region' s33_dma = 'district_metering_area' s34_sa = 'service_area' +s35_vd = 'virtual_district' TITLE = 'TITLE' JUNCTIONS = 'JUNCTIONS' diff --git a/test_tjnetwork.py b/test_tjnetwork.py index d345cdc..53026c1 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -6334,6 +6334,162 @@ class TestApi: self.leave(p) + def test_virtual_district(self): + p = 'test_virtual_district' + self.enter(p) + + vd = get_virtual_district(p, 'vd') + assert vd == {} + + add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)]})) + vd = get_virtual_district(p, 'vd') + assert vd == {} + + add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'center': 'j0'})) + vd = get_virtual_district(p, 'vd') + assert vd == {} + + add_junction(p, ChangeSet({'id': 'j0', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + + add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'center': 'j0', 'nodes' : ['x']})) + vd = get_virtual_district(p, 'vd') + assert vd == {} + + add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'center': 'j0', 'nodes': ['j0']})) + vd = get_virtual_district(p, 'vd') + assert vd['id'] == 'vd' + assert vd['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert vd['center'] == 'j0' + assert vd['nodes'] == ['j0'] + + add_junction(p, ChangeSet({'id': 'j1', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + + set_virtual_district(p, ChangeSet({'id': 'vd', 'center': 'j1', 'nodes': ['j1']})) + vd = get_virtual_district(p, 'vd') + assert vd['id'] == 'vd' + assert vd['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert vd['center'] == 'j1' + assert vd['nodes'] == ['j1'] + + assert get_all_virtual_district_ids(p) == ['vd'] + vds = get_all_virtual_districts(p) + assert len(vds) == 1 + vd = vds[0] + assert vd['id'] == 'vd' + assert vd['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert vd['center'] == 'j1' + assert vd['nodes'] == ['j1'] + + delete_virtual_district(p, ChangeSet({'id': 'vd'})) + vd = get_virtual_district(p, 'vd') + assert vd == {} + + self.leave(p) + + + def test_virtual_district_op(self): + p = 'test_virtual_district_op' + self.enter(p) + + cs = add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)]})).operations + assert len(cs) == 0 + + cs = add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'center': 'j0'})).operations + assert len(cs) == 0 + + add_junction(p, ChangeSet({'id': 'j0', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + + cs = add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'center': 'j0', 'nodes' : ['x']})).operations + assert len(cs) == 0 + + cs = add_virtual_district(p, ChangeSet({'id': 'vd', 'boundary': [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)], 'center': 'j0', 'nodes': ['j0']})).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['center'] == 'j0' + assert cs['nodes'] == ['j0'] + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['center'] == 'j0' + assert cs['nodes'] == ['j0'] + + add_junction(p, ChangeSet({'id': 'j1', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + + cs = set_virtual_district(p, ChangeSet({'id': 'vd', 'center': 'j1', 'nodes': ['j1']})).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['center'] == 'j1' + assert cs['nodes'] == ['j1'] + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['center'] == 'j0' + assert cs['nodes'] == ['j0'] + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['center'] == 'j1' + assert cs['nodes'] == ['j1'] + + cs = delete_virtual_district(p, ChangeSet({'id': 'vd'})).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + assert cs['boundary'] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)] + assert cs['center'] == 'j1' + assert cs['nodes'] == ['j1'] + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == 'virtual_district' + assert cs['id'] == 'vd' + + self.leave(p) + + + def test_virtual_district_gen(self): + p = 'test_virtual_district_gen' + read_inp(p, f'./inp/net3.inp', '3') + open_project(p) + + cs = generate_virtual_district(p, ['107', '139', '267', '211']).operations + assert len(cs) == 4 + + assert len(get_all_virtual_district_ids(p)) == 4 + assert len(get_all_virtual_districts(p)) == 4 + + cs = generate_virtual_district(p, ['107', '139', '267', '211']).operations + assert len(cs) == 8 + + assert len(get_all_virtual_district_ids(p)) == 4 + assert len(get_all_virtual_districts(p)) == 4 + + self.leave(p) + + # 36 water_distribution diff --git a/tjnetwork.py b/tjnetwork.py index 8941c32..ac12cb3 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -1104,28 +1104,28 @@ def calculate_virtual_district(name: str, centers: list[str]) -> dict[str, list[ return api.calculate_virtual_district(name, centers) def get_virtual_district_schema(name: str) -> dict[str, dict[str, Any]]: - return {} + return api.get_virtual_district_schema(name) def get_virtual_district(name: str, id: str) -> dict[str, Any]: - return {} + return api.get_virtual_district(name, id) def set_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet() + return api.set_virtual_district(name, cs) def add_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet() + return api.add_virtual_district(name, cs) def delete_virtual_district(name: str, cs: ChangeSet) -> ChangeSet: - return ChangeSet() + return api.delete_virtual_district(name, cs) def get_all_virtual_district_ids(name: str) -> list[str]: - return [] + return api.get_all_virtual_district_ids(name) def get_all_virtual_districts(name: str) -> list[dict[str, Any]]: - return [] + return api.get_all_virtual_districts(name) def generate_virtual_district(name: str, centers: list[str]) -> ChangeSet: - return ChangeSet() + return api.generate_virtual_district(name, centers) ############################################################