Support SA

This commit is contained in:
WQY\qiong
2023-05-16 21:30:23 +08:00
parent 47d2fe9ddd
commit 6046cdf01a
10 changed files with 434 additions and 34 deletions

View File

@@ -139,18 +139,17 @@ from .s31_scada_element import get_all_scada_element_ids, get_all_scada_elements
from .clean_api import clean_scada_element
from .s32_region_util import get_nodes_in_boundary, get_nodes_in_region, calculate_convex_hull, calculate_boundary, inflate_boundary, inflate_region
from .s32_region import get_region_schema, get_region, set_region, add_region, delete_region
from .s33_dma_cal import PARTITION_TYPE_RB, PARTITION_TYPE_KWAY
from .s33_dma_cal import calculate_district_metering_area_for_nodes, calculate_district_metering_area_for_region, calculate_district_metering_area_for_network
from .s33_dma import get_district_metering_area_schema, get_district_metering_area, set_district_metering_area, add_district_metering_area, delete_district_metering_area
from .s33_dma import get_all_district_metering_area_ids, get_all_district_metering_areas
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_gen import generate_service_area
from .s35_vd import calculate_virtual_district

View File

@@ -32,6 +32,7 @@ from .s30_scada_device_data import set_scada_device_data, add_scada_device_data,
from .s31_scada_element import set_scada_element, add_scada_element, delete_scada_element
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 .batch_api_cs import rewrite_batch_api
@@ -112,6 +113,8 @@ def _execute_add_command(name: str, cs: ChangeSet) -> ChangeSet:
return add_region(name, cs)
elif type == s33_dma:
return add_district_metering_area(name, cs)
elif type == s34_sa:
return add_service_area(name, cs)
return ChangeSet()
@@ -195,6 +198,8 @@ def _execute_update_command(name: str, cs: ChangeSet) -> ChangeSet:
return set_region(name, cs)
elif type == s33_dma:
return set_district_metering_area(name, cs)
elif type == s34_sa:
return set_service_area(name, cs)
return ChangeSet()
@@ -276,6 +281,8 @@ def _execute_delete_command(name: str, cs: ChangeSet) -> ChangeSet:
return delete_region(name, cs)
elif type == s33_dma:
return delete_district_metering_area(name, cs)
elif type == s34_sa:
return delete_service_area(name, cs)
return ChangeSet()

View File

@@ -7,7 +7,7 @@ def get_district_metering_area_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False },
'parent' : {'type': 'str' , 'optional': True , 'readonly': False },
'level' : {'type': 'str' , 'optional': False , 'readonly': True } }
'level' : {'type': 'int' , 'optional': False , 'readonly': True } }
def get_district_metering_area(name: str, id: str) -> dict[str, Any]:
@@ -29,7 +29,7 @@ def get_district_metering_area(name: str, id: str) -> dict[str, Any]:
return dma
# no update for nodes
def _set_district_metering_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']

217
api/s34_sa.py Normal file
View File

@@ -0,0 +1,217 @@
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_service_area_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'boundary' : {'type': 'tuple_list' , 'optional': False , 'readonly': False },
'source' : {'type': 'str' , 'optional': False , 'readonly': False },
'time_index' : {'type': 'int' , 'optional': False , 'readonly': False } }
def get_service_area(name: str, id: str) -> dict[str, Any]:
sa = get_region(name, id)
if sa == {}:
return {}
r = try_read(name, f"select * from region_sa where id = '{id}'")
if r == None:
return {}
sa['source'] = r['source']
sa['nodes'] = list(eval(r['nodes']))
sa['time_index'] = r['time_index']
return sa
def _set_service_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
new_boundary = cs.operations[0]['boundary']
old_boundary = get_region(name, id)['boundary']
new_source = cs.operations[0]['source']
f_new_source = f"'{new_source}'"
new_nodes = cs.operations[0]['nodes']
str_new_nodes = str(new_nodes).replace("'", "''")
new_time_index = cs.operations[0]['time_index']
old = get_service_area(name, id)
old_source = old['source']
f_old_source = f"'{old_source}'"
old_nodes = old['nodes']
str_old_nodes = str(old_nodes).replace("'", "''")
old_time_index = old['time_index']
redo_sql = f"update region set boundary = st_geomfromtext('{to_postgis_polygon(new_boundary)}') where id = '{id}';"
redo_sql += f"update region_sa set time_index = {new_time_index}, source = {f_new_source}, nodes = '{str_new_nodes}' where id = '{id}';"
undo_sql = f"update region_sa set time_index = {old_time_index}, source = {f_old_source}, 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': 'service_area', 'id': id, 'boundary': new_boundary, 'time_index': new_time_index, 'source': new_source, 'nodes': new_nodes }
undo_cs = g_update_prefix | { 'type': 'service_area', 'id': id, 'boundary': old_boundary, 'time_index': old_time_index, 'source': old_source, 'nodes': old_nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def set_service_area(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()
sa = get_service_area(name, op['id'])
if sa == {}:
return ChangeSet()
if 'boundary' not in op:
op['boundary'] = sa['boundary']
else:
b = op['boundary']
if len(b) < 4 or b[0] != b[-1]:
return ChangeSet()
if 'time_index' not in op:
op['time_index'] = sa['time_index']
if 'source' not in op:
op['source'] = sa['source']
if not is_node(name, op['source']):
return ChangeSet()
if 'nodes' not in op:
op['nodes'] = sa['nodes']
else:
for node in op['nodes']:
if not is_node(name, node):
return ChangeSet()
return execute_command(name, _set_service_area(name, cs))
def _add_service_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
boundary = cs.operations[0]['boundary']
time_index = cs.operations[0]['time_index']
source = cs.operations[0]['source']
f_source = f"'{source}'"
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)}', 'SA');"
redo_sql += f"insert into region_sa (id, time_index, source, nodes) values ('{id}', {time_index}, {f_source}, '{str_nodes}');"
undo_sql = f"delete from region_sa where id = '{id}';"
undo_sql += f"delete from region where id = '{id}';"
redo_cs = g_add_prefix | { 'type': 'service_area', 'id': id, 'boundary': boundary, 'time_index': time_index, 'source': source, 'nodes': nodes }
undo_cs = g_delete_prefix | { 'type': 'service_area', 'id': id }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def add_service_area(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()
sa = get_service_area(name, op['id'])
if sa != {}:
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 'time_index' not in op:
return ChangeSet()
if 'source' not in op:
return ChangeSet()
if not is_node(name, op['source']):
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_service_area(name, cs))
def _delete_service_area(name: str, cs: ChangeSet) -> DbChangeSet:
id = cs.operations[0]['id']
sa = get_service_area(name, id)
boundary = sa['boundary']
time_index = sa['time_index']
source = sa['source']
f_source = f"'{source}'"
nodes = sa['nodes']
str_nodes = str(nodes).replace("'", "''")
redo_sql = f"delete from region_sa 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)}', 'SA');"
undo_sql += f"insert into region_sa (id, time_index, source, nodes) values ('{id}', {time_index}, {f_source}, '{str_nodes}');"
redo_cs = g_delete_prefix | { 'type': 'service_area', 'id': id }
undo_cs = g_add_prefix | { 'type': 'service_area', 'id': id, 'boundary': boundary, 'time_index': time_index, 'source': source, 'nodes': nodes }
return DbChangeSet(redo_sql, undo_sql, [redo_cs], [undo_cs])
def delete_service_area(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()
sa = get_service_area(name, op['id'])
if sa == {}:
return ChangeSet()
return execute_command(name, _delete_service_area(name, cs))
def get_all_service_area_ids(name: str) -> list[str]:
ids = []
for row in read_all(name, f"select id from region_sa"):
ids.append(row['id'])
return ids
def get_all_service_areas(name: str) -> list[dict[str, Any]]:
result = []
for id in get_all_service_area_ids(name):
result.append(get_service_area(name, id))
return result

View File

@@ -2,18 +2,13 @@ import sys
import json
from queue import Queue
from .database import *
from .s0_base import get_node_links, get_link_nodes
from .s0_base import get_node_links, get_link_nodes, get_nodes
sys.path.append('..')
from epanet.epanet import run_project
def calculate_service_area(name: str, time_index: int = 0) -> dict[str, Any]:
inp = json.loads(run_project(name))
time_count = len(inp['node_results'][0]['result'])
if time_index >= time_count:
return {}
def _calculate_service_area(name: str, inp, time_index: int = 0) -> dict[str, list[str]]:
sources : dict[str, list[str]] = {}
for node_result in inp['node_results']:
result = node_result['result'][time_index]
@@ -43,6 +38,8 @@ def calculate_service_area(name: str, time_index: int = 0) -> dict[str, Any]:
elif node2 == cursor and link_flows[link] < 0:
queue.put(node1)
return sources
# calculation concentration
concentration_map: dict[str, dict[str, float]] = {}
node_wip: list[str] = []
@@ -85,6 +82,8 @@ def calculate_service_area(name: str, time_index: int = 0) -> dict[str, Any]:
break
if ready:
for link_node in up_link_nodes:
if link_node[1] not in concentration_map.keys():
continue
for source, concentration in concentration_map[link_node[1]].items():
concentration_map[node][source] += concentration * abs(link_flows[link_node[0]])
@@ -117,3 +116,17 @@ def calculate_service_area(name: str, time_index: int = 0) -> dict[str, Any]:
sas.append({ 'source': source, 'nodes': nodes })
return { 'service_areas' : sas, 'concentrations': concentration_map }
def calculate_service_area(name: str) -> list[dict[str, list[str]]]:
inp = json.loads(run_project(name))
result: list[dict[str, list[str]]] = []
time_count = len(inp['node_results'][0]['result'])
for i in range(time_count):
sas = _calculate_service_area(name, inp, i)
result.append(sas)
return result

18
api/s34_sa_gen.py Normal file
View File

@@ -0,0 +1,18 @@
from .s32_region_util import calculate_boundary, inflate_boundary
from .s34_sa_cal import *
from .batch_exe import execute_batch_command
def generate_service_area(name: str) -> ChangeSet:
cs = ChangeSet()
sass = calculate_service_area(name)
time_index = 0
for sas in sass:
for source, nodes in sas.items():
boundary = calculate_boundary(name, nodes)
boundary = inflate_boundary(name, boundary)
cs.add({ 'type': 'service_area', 'id': f"SA_{source}_{time_index}", 'boundary': boundary, 'time_index': time_index, 'source': source, 'nodes': nodes })
time_index += 1
return execute_batch_command(name, cs)

View File

@@ -35,6 +35,7 @@ s30_scada_device_data = 'scada_device_data'
s31_scada_element = 'scada_element'
s32_region = 'region'
s33_dma = 'district_metering_area'
s34_sa = 'service_area'
TITLE = 'TITLE'
JUNCTIONS = 'JUNCTIONS'