from typing import Any from ..core.database import ( ChangeSet, DatabaseCommand, execute_command, g_add_prefix, g_delete_prefix, g_update_prefix, sql_literal, try_read, ) from .region_geometry import from_postgis_polygon, to_postgis_polygon def get_region_schema(name: str) -> dict[str, dict[str, Any]]: return { "id": {"type": "str", "optional": False, "readonly": True}, "region_type": {"type": "str", "optional": False, "readonly": False}, "boundary": {"type": "tuple_list", "optional": False, "readonly": False}, } def get_region(name: str, id: str) -> dict[str, Any]: row = try_read( name, "select id, region_type, st_astext(boundary) as boundary_geom " "from gis.regions where id = %s", (id,), ) if row is None: return {} return { "id": str(row["id"]), "region_type": str(row["region_type"]), "boundary": from_postgis_polygon(str(row["boundary_geom"])), } def _valid_boundary(boundary: list[Any]) -> bool: return len(boundary) >= 4 and boundary[0] == boundary[-1] def _set_region(name: str, cs: ChangeSet) -> DatabaseCommand: region_id = cs.operations[0]["id"] old = get_region(name, region_id) new = old | { key: cs.operations[0][key] for key in ("region_type", "boundary") if key in cs.operations[0] } statement = ( "update gis.regions set " f"region_type = {sql_literal(new['region_type'])}, " f"boundary = st_geomfromtext({sql_literal(to_postgis_polygon(new['boundary']))}, 900914) " f"where id = {sql_literal(region_id)};" ) return DatabaseCommand( statement, [g_update_prefix | {"type": "region"} | new], ) def set_region(name: str, cs: ChangeSet) -> ChangeSet: operation = cs.operations[0] if "id" not in operation or get_region(name, operation["id"]) == {}: return ChangeSet() if "boundary" in operation and not _valid_boundary(operation["boundary"]): return ChangeSet() return execute_command(name, _set_region(name, cs)) def _add_region(name: str, cs: ChangeSet) -> DatabaseCommand: operation = cs.operations[0] region_id = operation["id"] region_type = str(operation.get("region_type", "none")) boundary = operation["boundary"] statement = ( "insert into gis.regions (id, region_type, boundary) values " f"({sql_literal(region_id)}, {sql_literal(region_type)}, " f"st_geomfromtext({sql_literal(to_postgis_polygon(boundary))}, 900914));" ) value = {"type": "region", "id": region_id, "region_type": region_type, "boundary": boundary} return DatabaseCommand( statement, [g_add_prefix | value], ) def add_region(name: str, cs: ChangeSet) -> ChangeSet: operation = cs.operations[0] if "id" not in operation or "boundary" not in operation: return ChangeSet() if not _valid_boundary(operation["boundary"]): return ChangeSet() if get_region(name, operation["id"]) != {}: return ChangeSet() return execute_command(name, _add_region(name, cs)) def _delete_region(name: str, cs: ChangeSet) -> DatabaseCommand: region_id = cs.operations[0]["id"] statement = f"delete from gis.regions where id = {sql_literal(region_id)};" return DatabaseCommand( statement, [g_delete_prefix | {"type": "region", "id": region_id}], ) def delete_region(name: str, cs: ChangeSet) -> ChangeSet: if "id" not in cs.operations[0] or get_region(name, cs.operations[0]["id"]) == {}: return ChangeSet() return execute_command(name, _delete_region(name, cs)) def inp_in_region(line: str) -> str: tokens = line.split() return f"insert into gis.regions (id, region_type) values ({sql_literal(tokens[0])}, {sql_literal(tokens[1])});" def inp_in_bound(line: str) -> str: return line.split()[0] def inp_in_regionnodes(line: str) -> str: tokens = line.split() return f"insert into gis.region_nodes (region_id, node_id) values ({sql_literal(tokens[0])}, {sql_literal(tokens[1])});"