From 9be8b417858c9d1725873b316041c0b13445a8b5 Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Wed, 13 Mar 2024 23:27:00 +0800 Subject: [PATCH 01/10] Add new serialization modules and tests --- src/__init__ | 0 src/__init__.py | 0 src/serialization.py | 0 test/example.json | 14 ++++++++ test/test_deserialization.py | 0 test/test_hypergraph.json | 62 ++++++++++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+) create mode 100644 src/__init__ create mode 100644 src/__init__.py create mode 100644 src/serialization.py create mode 100644 test/example.json create mode 100644 test/test_deserialization.py create mode 100644 test/test_hypergraph.json diff --git a/src/__init__ b/src/__init__ new file mode 100644 index 0000000..e69de29 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/serialization.py b/src/serialization.py new file mode 100644 index 0000000..e69de29 diff --git a/test/example.json b/test/example.json new file mode 100644 index 0000000..f2161ab --- /dev/null +++ b/test/example.json @@ -0,0 +1,14 @@ +{ + "name": "$name$", + "version": "$version$", + "description": "$END$", + "main": "$main$", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/Knight0132/indoorjson3-python.git" + }, + "private": true +} diff --git a/test/test_deserialization.py b/test/test_deserialization.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_hypergraph.json b/test/test_hypergraph.json new file mode 100644 index 0000000..7c41948 --- /dev/null +++ b/test/test_hypergraph.json @@ -0,0 +1,62 @@ +{ + "hyperNodes": [ + { + "$id": "conn1-2", + "properties": { + "type": "door", + "\u5f00\u653e\u65f6\u95f4": "\u5168\u5929", + "\uc624\ud508 \uc2dc\uac04": "\ud558\ub8e8 \uc885\uc77c" + }, + "source": "c1", + "target": "c2", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + }, + { + "$id": "conn3-1", + "properties": { + "type": "window" + }, + "source": "c3", + "target": "c1", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + } + ], + "hyperEdges": [ + { + "id": "c1", + "properties": { + "roomNumber": "1101" + }, + "space": "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", + "node": "POINT (0.5 0.5)", + "inner_nodelist": [ + "conn1-2", + "conn3-1" + ] + }, + { + "id": "c2", + "properties": { + "roomNumber": "1102" + }, + "space": "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", + "node": "POINT (1.5 0.5)", + "inner_nodelist": [ + "conn1-2" + ] + }, + { + "id": "c3", + "properties": { + "roomNumber": "1103" + }, + "space": "POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))", + "node": "POINT (0.5 1.5)", + "inner_nodelist": [ + "conn3-1" + ] + } + ] +} \ No newline at end of file From a85e20aa3c256c8b477818fb99b5a0d75c1be2b8 Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Wed, 13 Mar 2024 23:29:28 +0800 Subject: [PATCH 02/10] Update serialization and test modifications, remove unused __init__ --- src/serialization.py | 237 +++++++++++++++++++++++++++++++++++ test/example.json | 76 +++++++++-- test/test_deserialization.py | 25 ++++ 3 files changed, 327 insertions(+), 11 deletions(-) diff --git a/src/serialization.py b/src/serialization.py index e69de29..2b191c6 100644 --- a/src/serialization.py +++ b/src/serialization.py @@ -0,0 +1,237 @@ +""" +File Name: serialization.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/13 +""" + +import numpy as np +from shapely.wkt import loads + + +class Graph: + + def __init__(self): + self._properties = [] + self._cells = [] + self._connections = [] + self._layers = [] + self._rlineses = [] + self._hypergraph = {} + + @property + def properties(self): + return self._properties + + @property + def cells(self): + return self._cells + + @property + def connections(self): + return self._connections + + @property + def layers(self): + return self._layers + + @property + def rlineses(self): + return self._rlineses + + @property + def hypergraph(self): + return self._hypergraph + + class Cell: + + def __init__(self, cell_id, properties, space, node): + self.__id = cell_id + self._properties = properties + self.__space = space + self.__node = node + + @property + def id(self): + return self.__id + + @property + def properties(self): + return self._properties + + @property + def space(self): + return self.__space + + @property + def node(self): + return self.__node + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['properties'], + loads(json_data['space']), loads(json_data['node'])) + + def to_json(self): + return { + '$id': self.__id, + 'properties': self._properties, + 'space': self.__space.wkt, + 'node': self.__node.wkt + } + + class Connection: + + def __init__(self, connections_id, properties, source, target, bound, + edge): + self.__id = connections_id + self._properties = properties + self.__source = source + self.__target = target + self.__bound = bound + self.__edge = edge + + @property + def id(self): + return self.__id + + @property + def properties(self): + return self._properties + + @property + def source(self): + return self.__source + + @property + def target(self): + return self.__target + + @property + def bound(self): + return self.__bound + + @property + def edge(self): + return self.__edge + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['properties'], + json_data['fr'], json_data['to'], + loads(json_data['bound']), loads(json_data['edge'])) + + def to_json(self): + return { + '$id': self.__id, + 'properties': self._properties, + 'source': self.__source, + 'target': self.__target, + 'bound': self.__bound.wkt, + 'edge': self.__edge.wkt + } + + def add_cell(self, cell: Cell): + if cell.id not in [c.id for c in self._cells]: + self._cells.append(cell) + else: + raise ValueError('Cell id already exists') + + def add_connection(self, connection: Connection): + if connection.id not in [c.id for c in self._connections]: + if connection.source in [ + c.id for c in self._cells + ] and connection.target in [c.id for c in self._cells]: + self._connections.append(connection) + elif connection.source not in [ + c.id for c in self._cells + ] and connection.target in [c.id for c in self._cells]: + raise ValueError('Source cell does not exist') + elif connection.source in [ + c.id for c in self._cells + ] and connection.target not in [c.id for c in self._cells]: + raise ValueError('Target cell does not exist') + else: + raise ValueError('Source and target cell do not exist') + else: + raise ValueError('Connection id already exists') + + def get_incident_matrix(self): + cells = self.cells + connections = self.connections + incident_matrix = np.zeros((len(cells), len(connections)), dtype=int) + for j, connections in enumerate(connections): + source = self.get_cell_from_id(connections.source) + target = self.get_cell_from_id(connections.target) + source_index = cells.index(source) + target_index = cells.index(target) + incident_matrix[source_index, j] = 1 + incident_matrix[target_index, j] = -1 + return incident_matrix + + def get_hypergraph(self): + cells = self.cells + connections = self.connections + hypergraph = self._hypergraph + hypergraph['hyperNodes'] = [] + hypergraph['hyperEdges'] = [] + incident_matrix = self.get_incident_matrix() + incident_matrix_transpose = incident_matrix.T + + for hyperNode in connections: + hypergraph['hyperNodes'].append(hyperNode.to_json()) + + for j in range(incident_matrix_transpose.shape[1]): + hyperEdge = {} + node_id_list = [] + for i in range(incident_matrix_transpose.shape[0]): + if incident_matrix_transpose[i, j] != 0: + inner_edge_id = connections[i].id + node_id_list.append(inner_edge_id) + hyperEdge['id'] = cells[j].id + hyperEdge['properties'] = cells[j].properties + hyperEdge['space'] = cells[j].space.wkt + hyperEdge['node'] = cells[j].node.wkt + hyperEdge['inner_nodelist'] = node_id_list + hypergraph['hyperEdges'].append(hyperEdge) + return hypergraph + + def get_cell_from_id(self, cell_id): + for cell in self.cells: + if cell.id == cell_id: + return cell + return None + + def get_connection_from_id(self, connection_id): + for connection in self.connections: + if connection.id == connection_id: + return connection + return None + + def to_json(self): + return { + 'properties': self._properties, + 'cells': [cell.to_json() for cell in self._cells], + 'connections': [ + connection.to_json() for connection in self._connections + ], + 'layers': self._layers, + 'rlineses': self._rlineses + } + + @staticmethod + def from_json(json_data): + graph = Graph() + graph._properties = json_data['properties'] + graph._cells = [ + Graph.Cell.from_json(cell) for cell in json_data['cells'] + ] + graph._connections = [ + Graph.Connection.from_json(connection) + for connection in json_data['connections'] + ] + graph._layers = json_data['layers'] + graph._rlineses = json_data['rlineses'] + return graph diff --git a/test/example.json b/test/example.json index f2161ab..ff44232 100644 --- a/test/example.json +++ b/test/example.json @@ -1,14 +1,68 @@ { - "name": "$name$", - "version": "$version$", - "description": "$END$", - "main": "$main$", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "properties": { + "name": "indoorjson3-cpp", + "labels": ["indoorgml", "GIS"], + "language": ["English", "中文", "한국어"], + "author": { + "name": "Kunlin Yu", + "email": "yukunlin@syriusrobotics.com" + } }, - "repository": { - "type": "git", - "url": "https://github.com/Knight0132/indoorjson3-python.git" - }, - "private": true + "cells": [ + { + "$id": "c1", + "properties": {"roomNumber": "1101"}, + "space": "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", + "node": "POINT (0.5 0.5)" + }, + { + "$id": "c2", + "properties": {"roomNumber": "1102"}, + "space": "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", + "node": "POINT (1.5 0.5)" + }, + { + "$id": "c3", + "properties": {"roomNumber": "1103"}, + "space": "POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))", + "node": "POINT (0.5 1.5)" + } + ], + "connections": [ + { + "$id": "conn1-2", + "properties": { + "type": "door", + "开放时间": "全天", + "오픈 시간": "하루 종일" + }, + "fr": "c1", + "to": "c2", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + }, + { + "$id": "conn3-1", + "properties": {"type": "window"}, + "fr": "c3", + "to": "c1", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + } + ], + "layers": [ + { + "$id": "layer", + "cells": ["c1", "c2"] + } + ], + "rlineses": [ + { + "$id": "rlines1", + "cell": "c1", + "ins": ["conn3-1"], + "outs": ["conn1-2"], + "closure": [] + } + ] } diff --git a/test/test_deserialization.py b/test/test_deserialization.py index e69de29..a569b4f 100644 --- a/test/test_deserialization.py +++ b/test/test_deserialization.py @@ -0,0 +1,25 @@ +""" +File Name: test_deserialization.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/13 +""" + +import json +import sys +import os + +sys.path.append(os.path.abspath('../src')) + +from serialization import Graph + +with open('example.json', 'r', encoding='utf-8') as f: + graph_dict = json.load(f) + +graph = Graph().from_json(graph_dict) +hypergraph = graph.get_hypergraph() + +with open('test_hypergraph.json', 'w', encoding='utf-8') as f: + json.dump(hypergraph, f, indent=4) From 045b5950c134b7ed2fa1442ca8229ce80b5c1535 Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Thu, 14 Mar 2024 11:14:44 +0800 Subject: [PATCH 03/10] add test for serialization --- .DS_Store | Bin 0 -> 6148 bytes src/__init__ | 0 src/serialization.py | 97 +++++++++++++++++++++++++++++++++++--- test/test_create.py | 96 +++++++++++++++++++++++++++++++++++++ test/test_created.json | 76 +++++++++++++++++++++++++++++ test/test_hypergraph.json | 30 ++++++++---- 6 files changed, 283 insertions(+), 16 deletions(-) create mode 100644 .DS_Store delete mode 100644 src/__init__ create mode 100644 test/test_create.py create mode 100644 test/test_created.json diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..82bb6270c500770552092dcc80368b9eca5d28ae GIT binary patch literal 6148 zcmeHK%}(1u5S}G&*?=nL&`KOv;u_KXw1KJ@H-sCf8qouw5aTFVINm6BnnM)H=W^v8 zcm*WRh#Tq&UWb|84I%`3sRX6kk!HX3{H#5n>>V!vu=*e<0~7#&qY^p`I6NZcC!LWw zV<9J_HKI=0JCNPpOr$kf1}p>rA_Mg8IY%b@#c{|VN`6A!*4&~5GTj?OF zxB5GDwIyZbPv$LuJ8Tb|h4ZwJ9wVwn}wy( zsJ2=uiqGZMaZ!xER7yqhsahM4^Zdi|$FJX;M^QJHzpzpihP#}ScLpbLj1yDMPp=Rl z1S+9xFw>~E4jfb?M|9Av1m|>?pd4Y)HJE8c4+_(%h&q*-BL>syXcs2VHJEAC>A>_a z1Tb@FW=<$f-5u8#Djb-r(Tr8RfYX#~)Dhb798dVAorX8yZwc;gICFm10L39mf8qtDce*_c_cCZXQD+9N`)S_|# literal 0 HcmV?d00001 diff --git a/src/__init__ b/src/__init__ deleted file mode 100644 index e69de29..0000000 diff --git a/src/serialization.py b/src/serialization.py index 2b191c6..f72d097 100644 --- a/src/serialization.py +++ b/src/serialization.py @@ -133,6 +133,73 @@ def to_json(self): 'edge': self.__edge.wkt } + class Layer: + def __init__(self, layer_id, cells): + self.__id = layer_id + self.__cells = cells + + @property + def id(self): + return self.__id + + @property + def cells(self): + return self.__cells + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['cells']) + + def to_json(self): + return { + '$id': self.__id, + 'cells': self.__cells + } + + class Rlines: + def __init__(self, rlines_id, cells, ins, outs, closure): + self.__id = rlines_id + self.__cells = cells + self.__ins = ins + self.__outs = outs + self.__closure = closure + + @property + def id(self): + return self.__id + + @property + def cells(self): + return self.__cells + + @property + def ins(self): + return self.__ins + + @property + def outs(self): + return self.__outs + + @property + def closure(self): + return self.__closure + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['cells'], json_data['ins'], json_data['outs'], json_data['closure']) + + def to_json(self): + return { + '$id': self.__id, + 'cells': self.__cells, + 'ins': self.__ins, + 'outs': self.__outs, + 'closure': self.__closure + } + + def set_properties(self, properties: dict): + self._properties.append(properties) + def add_cell(self, cell: Cell): if cell.id not in [c.id for c in self._cells]: self._cells.append(cell) @@ -158,6 +225,12 @@ def add_connection(self, connection: Connection): else: raise ValueError('Connection id already exists') + def set_layers(self, layers: Layer): + self._layers.append(layers) + + def set_rlineses(self, rlineses: Rlines): + self._rlineses.append(rlineses) + def get_incident_matrix(self): cells = self.cells connections = self.connections @@ -185,19 +258,31 @@ def get_hypergraph(self): for j in range(incident_matrix_transpose.shape[1]): hyperEdge = {} - node_id_list = [] + inner_edge_id = {"ins": [], "outs": []} for i in range(incident_matrix_transpose.shape[0]): if incident_matrix_transpose[i, j] != 0: - inner_edge_id = connections[i].id - node_id_list.append(inner_edge_id) + if incident_matrix_transpose[i, j] == -1: + inner_edge_ins_id = connections[i].id + inner_edge_id["ins"].append(inner_edge_ins_id) + elif incident_matrix_transpose[i, j] == 1: + inner_edge_outs_id = connections[i].id + inner_edge_id["outs"].append(inner_edge_outs_id) + else: + raise ValueError('Incident matrix error') hyperEdge['id'] = cells[j].id hyperEdge['properties'] = cells[j].properties hyperEdge['space'] = cells[j].space.wkt hyperEdge['node'] = cells[j].node.wkt - hyperEdge['inner_nodelist'] = node_id_list + hyperEdge['inner_nodeset'] = inner_edge_id hypergraph['hyperEdges'].append(hyperEdge) + + self.set_hypergraph(hypergraph) + return hypergraph + def set_hypergraph(self, hypergraph): + self._hypergraph = hypergraph + def get_cell_from_id(self, cell_id): for cell in self.cells: if cell.id == cell_id: @@ -217,8 +302,8 @@ def to_json(self): 'connections': [ connection.to_json() for connection in self._connections ], - 'layers': self._layers, - 'rlineses': self._rlineses + 'layers': [layer.to_json() for layer in self._layers], + 'rlineses': [rlineses.to_json() for rlineses in self._rlineses] } @staticmethod diff --git a/test/test_create.py b/test/test_create.py new file mode 100644 index 0000000..468421e --- /dev/null +++ b/test/test_create.py @@ -0,0 +1,96 @@ +""" +File Name: test_create.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/14 +""" + +import json +from shapely.wkt import loads +import sys +import os + +sys.path.append(os.path.abspath('../src')) + +from serialization import Graph + +graph = Graph() + +properties = { + "name": "indoorjson3-python", + "labels": ["indoorgml", "GIS"], + "language": ["English", "中文", "한국어"], + "author": {"name": "Ziwei Xiang", "email": "knightzz1016@gmail.com"} +} + +cells = [] + +c1_id = "c1" +c1_properties = {"roomNumber": "1101"} +c1_space = loads("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") +c1_node = loads("POINT (0.5 0.5)") +cell1 = graph.Cell(c1_id, c1_properties, c1_space, c1_node) +cells.append(cell1) + +c2_id = "c2" +c2_properties = {"roomNumber": "1102"} +c2_space = loads("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))") +c2_node = loads("POINT (1.5 0.5)") +cell2 = graph.Cell(c2_id, c2_properties, c2_space, c2_node) +cells.append(cell2) + +c3_id = "c3" +c3_properties = {"roomNumber": "1103"} +c3_space = loads("POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))") +c3_node = loads("POINT (0.5 1.5)") +cell3 = graph.Cell(c3_id, c3_properties, c3_space, c3_node) +cells.append(cell3) + +for cell in cells: + graph.add_cell(cell) + +connections = [] + +con1_id = "conn1-2" +con1_properties = {"type": "door", "开放时间": "全天", "오픈 시간": "하루 종일"} +con1_fr = c1_id +con1_to = c2_id +con1_bound = loads("LINESTRING (1 0, 1 1)") +con1_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") +connection1 = graph.Connection(con1_id, con1_properties, con1_fr, con1_to, con1_bound, con1_edge) +connections.append(connection1) + +con2_id = "conn3-1" +con2_properties = {"type": "window"} +con2_fr = c3_id +con2_to = c1_id +con2_bound = loads("LINESTRING (1 0, 1 1)") +con2_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") +connection2 = graph.Connection(con2_id, con2_properties, con2_fr, con2_to, con2_bound, con2_edge) +connections.append(connection2) + +for connection in connections: + graph.add_connection(connection) + +layer_id = "layer" +layer_cells = [cell.id for cell in cells] +layer = graph.Layer(layer_id, layer_cells) +graph.set_layers(layer) + +hypergraph = graph.get_hypergraph() +for hyperEdge in hypergraph['hyperEdges']: + i = 0 + if hyperEdge["inner_nodeset"]["ins"] and hyperEdge["inner_nodeset"]["outs"]: + i += 1 + rlines_id = str("rlines") + str(i) + rlines_cell = hyperEdge["id"] + rlines_ins = hyperEdge["inner_nodeset"]["ins"] + rlines_outs = hyperEdge["inner_nodeset"]["outs"] + rlines_closure = [] + rlines = graph.Rlines(rlines_id, rlines_cell, rlines_ins, rlines_outs, rlines_closure) + graph.set_rlineses(rlines) + +with open('test_created.json', 'w', encoding='utf-8') as f: + json.dump(graph.to_json(), f, indent=4) diff --git a/test/test_created.json b/test/test_created.json new file mode 100644 index 0000000..f9edd35 --- /dev/null +++ b/test/test_created.json @@ -0,0 +1,76 @@ +{ + "properties": [], + "cells": [ + { + "$id": "c1", + "properties": { + "roomNumber": "1101" + }, + "space": "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", + "node": "POINT (0.5 0.5)" + }, + { + "$id": "c2", + "properties": { + "roomNumber": "1102" + }, + "space": "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", + "node": "POINT (1.5 0.5)" + }, + { + "$id": "c3", + "properties": { + "roomNumber": "1103" + }, + "space": "POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))", + "node": "POINT (0.5 1.5)" + } + ], + "connections": [ + { + "$id": "conn1-2", + "properties": { + "type": "door", + "\u5f00\u653e\u65f6\u95f4": "\u5168\u5929", + "\uc624\ud508 \uc2dc\uac04": "\ud558\ub8e8 \uc885\uc77c" + }, + "source": "c1", + "target": "c2", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + }, + { + "$id": "conn3-1", + "properties": { + "type": "window" + }, + "source": "c3", + "target": "c1", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + } + ], + "layers": [ + { + "$id": "layer", + "cells": [ + "c1", + "c2", + "c3" + ] + } + ], + "rlineses": [ + { + "$id": "rlines1", + "cells": "c1", + "ins": [ + "conn3-1" + ], + "outs": [ + "conn1-2" + ], + "closure": [] + } + ] +} \ No newline at end of file diff --git a/test/test_hypergraph.json b/test/test_hypergraph.json index 7c41948..d122b8b 100644 --- a/test/test_hypergraph.json +++ b/test/test_hypergraph.json @@ -31,10 +31,14 @@ }, "space": "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "node": "POINT (0.5 0.5)", - "inner_nodelist": [ - "conn1-2", - "conn3-1" - ] + "inner_nodeset": { + "ins": [ + "conn3-1" + ], + "outs": [ + "conn1-2" + ] + } }, { "id": "c2", @@ -43,9 +47,12 @@ }, "space": "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", "node": "POINT (1.5 0.5)", - "inner_nodelist": [ - "conn1-2" - ] + "inner_nodeset": { + "ins": [ + "conn1-2" + ], + "outs": [] + } }, { "id": "c3", @@ -54,9 +61,12 @@ }, "space": "POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))", "node": "POINT (0.5 1.5)", - "inner_nodelist": [ - "conn3-1" - ] + "inner_nodeset": { + "ins": [], + "outs": [ + "conn3-1" + ] + } } ] } \ No newline at end of file From b09fa0ff9f6830c00d76915e2232526260f79f4f Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Thu, 14 Mar 2024 18:38:37 +0800 Subject: [PATCH 04/10] spilt all classes into different files --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 + src/cell.py | 48 ++++++ src/connection.py | 62 +++++++ src/indoorspace.py | 180 ++++++++++++++++++++ src/layer.py | 32 ++++ src/rlines.py | 50 ++++++ src/serialization.py | 319 ++--------------------------------- test/test_create.py | 59 ++++--- test/test_created.json | 2 +- test/test_deserialization.py | 10 +- 11 files changed, 425 insertions(+), 340 deletions(-) delete mode 100644 .DS_Store create mode 100644 src/cell.py create mode 100644 src/connection.py create mode 100644 src/indoorspace.py create mode 100644 src/layer.py create mode 100644 src/rlines.py diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 82bb6270c500770552092dcc80368b9eca5d28ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}(1u5S}G&*?=nL&`KOv;u_KXw1KJ@H-sCf8qouw5aTFVINm6BnnM)H=W^v8 zcm*WRh#Tq&UWb|84I%`3sRX6kk!HX3{H#5n>>V!vu=*e<0~7#&qY^p`I6NZcC!LWw zV<9J_HKI=0JCNPpOr$kf1}p>rA_Mg8IY%b@#c{|VN`6A!*4&~5GTj?OF zxB5GDwIyZbPv$LuJ8Tb|h4ZwJ9wVwn}wy( zsJ2=uiqGZMaZ!xER7yqhsahM4^Zdi|$FJX;M^QJHzpzpihP#}ScLpbLj1yDMPp=Rl z1S+9xFw>~E4jfb?M|9Av1m|>?pd4Y)HJE8c4+_(%h&q*-BL>syXcs2VHJEAC>A>_a z1Tb@FW=<$f-5u8#Djb-r(Tr8RfYX#~)Dhb798dVAorX8yZwc;gICFm10L39mf8qtDce*_c_cCZXQD+9N`)S_|# diff --git a/.gitignore b/.gitignore index 68bc17f..8dae204 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ __pycache__/ # C extensions *.so +.DS_Store + + # Distribution / packaging .Python build/ diff --git a/src/cell.py b/src/cell.py new file mode 100644 index 0000000..6fbbf10 --- /dev/null +++ b/src/cell.py @@ -0,0 +1,48 @@ +""" +File Name: cell.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/14 +""" + +from shapely.wkt import loads + + +class Cell: + + def __init__(self, cell_id, properties, space, node): + self.__id = cell_id + self._properties = properties + self.__space = space + self.__node = node + + @property + def id(self): + return self.__id + + @property + def properties(self): + return self._properties + + @property + def space(self): + return self.__space + + @property + def node(self): + return self.__node + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['properties'], + loads(json_data['space']), loads(json_data['node'])) + + def to_json(self): + return { + '$id': self.__id, + 'properties': self._properties, + 'space': self.__space.wkt, + 'node': self.__node.wkt + } diff --git a/src/connection.py b/src/connection.py new file mode 100644 index 0000000..e4cc4c8 --- /dev/null +++ b/src/connection.py @@ -0,0 +1,62 @@ +""" +File Name: connection.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/14 +""" + +from shapely.wkt import loads + + +class Connection: + + def __init__(self, connections_id, properties, source, target, bound, + edge): + self.__id = connections_id + self._properties = properties + self.__source = source + self.__target = target + self.__bound = bound + self.__edge = edge + + @property + def id(self): + return self.__id + + @property + def properties(self): + return self._properties + + @property + def source(self): + return self.__source + + @property + def target(self): + return self.__target + + @property + def bound(self): + return self.__bound + + @property + def edge(self): + return self.__edge + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['properties'], + json_data['fr'], json_data['to'], + loads(json_data['bound']), loads(json_data['edge'])) + + def to_json(self): + return { + '$id': self.__id, + 'properties': self._properties, + 'source': self.__source, + 'target': self.__target, + 'bound': self.__bound.wkt, + 'edge': self.__edge.wkt + } diff --git a/src/indoorspace.py b/src/indoorspace.py new file mode 100644 index 0000000..d31451a --- /dev/null +++ b/src/indoorspace.py @@ -0,0 +1,180 @@ +""" +File Name: indoorspace.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/14 +""" + +import numpy as np +from cell import Cell +from connection import Connection +from layer import Layer +from rlines import Rlines + + +class IndoorSpace: + + def __init__(self): + self._properties = [] + self._cells = [] + self._connections = [] + self._layers = [] + self._rlineses = [] + self._hypergraph = {} + + @property + def properties(self): + return self._properties + + @property + def cells(self): + return self._cells + + @property + def connections(self): + return self._connections + + @property + def layers(self): + return self._layers + + @property + def rlineses(self): + return self._rlineses + + @property + def hypergraph(self): + return self._hypergraph + + def set_properties(self, properties: dict): + self._properties.append(properties) + + def add_cell(self, cell: Cell): + if cell.id not in [c.id for c in self._cells]: + self._cells.append(cell) + else: + raise ValueError('Cell id already exists') + + def add_connection(self, connection: Connection): + if connection.id not in [c.id for c in self._connections]: + if connection.source in [ + c.id for c in self._cells + ] and connection.target in [c.id for c in self._cells]: + self._connections.append(connection) + elif connection.source not in [ + c.id for c in self._cells + ] and connection.target in [c.id for c in self._cells]: + raise ValueError('Source cell does not exist') + elif connection.source in [ + c.id for c in self._cells + ] and connection.target not in [c.id for c in self._cells]: + raise ValueError('Target cell does not exist') + else: + raise ValueError('Source and target cell do not exist') + else: + raise ValueError('Connection id already exists') + + def set_layers(self, layers: Layer): + self._layers.append(layers) + + def set_rlineses(self, rlineses: Rlines): + self._rlineses.append(rlineses) + + def get_incident_matrix(self): + cells = self.cells + connections = self.connections + incident_matrix = np.zeros((len(cells), len(connections)), dtype=int) + for j, connections in enumerate(connections): + source = self.get_cell_from_id(connections.source) + target = self.get_cell_from_id(connections.target) + source_index = cells.index(source) + target_index = cells.index(target) + incident_matrix[source_index, j] = 1 + incident_matrix[target_index, j] = -1 + return incident_matrix + + def get_hypergraph_incidence_matrix(self): + return self.get_incident_matrix().T + + def get_hypergraph(self): + cells = self.cells + connections = self.connections + hypergraph = self._hypergraph + hypergraph['hyperNodes'] = [] + hypergraph['hyperEdges'] = [] + incident_matrix = self.get_incident_matrix() + incident_matrix_transpose = incident_matrix.T + + for hyperNode in connections: + hypergraph['hyperNodes'].append(hyperNode.to_json()) + + for j in range(incident_matrix_transpose.shape[1]): + hyperEdge = {} + inner_edge_id = {'ins': [], 'outs': []} + for i in range(incident_matrix_transpose.shape[0]): + if incident_matrix_transpose[i, j] != 0: + if incident_matrix_transpose[i, j] == -1: + inner_edge_ins_id = connections[i].id + inner_edge_id['ins'].append(inner_edge_ins_id) + elif incident_matrix_transpose[i, j] == 1: + inner_edge_outs_id = connections[i].id + inner_edge_id['outs'].append(inner_edge_outs_id) + else: + raise ValueError('Incident matrix error') + hyperEdge['id'] = cells[j].id + hyperEdge['properties'] = cells[j].properties + hyperEdge['space'] = cells[j].space.wkt + hyperEdge['node'] = cells[j].node.wkt + hyperEdge['inner_nodeset'] = inner_edge_id + hypergraph['hyperEdges'].append(hyperEdge) + + self.set_hypergraph(hypergraph) + + return hypergraph + + def set_hypergraph(self, hypergraph): + self._hypergraph = hypergraph + + def get_cell_from_id(self, cell_id): + for cell in self.cells: + if cell.id == cell_id: + return cell + return None + + def get_connection_from_id(self, connection_id): + for connection in self.connections: + if connection.id == connection_id: + return connection + return None + + def to_json(self): + return { + 'properties': self._properties, + 'cells': [cell.to_json() for cell in self._cells], + 'connections': [ + connection.to_json() for connection in self._connections + ], + 'layers': [layer.to_json() for layer in self._layers], + 'rlineses': [rlines.to_json() for rlines in self._rlineses] + } + + @staticmethod + def from_json(json_data): + indoorSpace = IndoorSpace() + + setattr(indoorSpace, '_properties', json_data['properties']) + + class_map = { + '_cells': Cell, + '_connections': Connection, + '_layers': Layer, + '_rlineses': Rlines + } + + for attr, cls in class_map.items(): + items = json_data[attr.lstrip('_')] + setattr(indoorSpace, attr, [cls.from_json(item) for item in items]) + + return indoorSpace diff --git a/src/layer.py b/src/layer.py new file mode 100644 index 0000000..582673d --- /dev/null +++ b/src/layer.py @@ -0,0 +1,32 @@ +""" +File Name: layer.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/14 +""" + + +class Layer: + def __init__(self, layer_id, cells): + self.__id = layer_id + self.__cells = cells + + @property + def id(self): + return self.__id + + @property + def cells(self): + return self.__cells + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['cells']) + + def to_json(self): + return { + '$id': self.__id, + 'cells': self.__cells + } diff --git a/src/rlines.py b/src/rlines.py new file mode 100644 index 0000000..1bb9b91 --- /dev/null +++ b/src/rlines.py @@ -0,0 +1,50 @@ +""" +File Name: rlines.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/14 +""" + + +class Rlines: + def __init__(self, rlines_id, cells, ins, outs, closure): + self.__id = rlines_id + self.__cells = cells + self.__ins = ins + self.__outs = outs + self.__closure = closure + + @property + def id(self): + return self.__id + + @property + def cells(self): + return self.__cells + + @property + def ins(self): + return self.__ins + + @property + def outs(self): + return self.__outs + + @property + def closure(self): + return self.__closure + + @classmethod + def from_json(cls, json_data): + return cls(json_data['$id'], json_data['cell'], json_data['ins'], json_data['outs'], json_data['closure']) + + def to_json(self): + return { + '$id': self.__id, + 'cell': self.__cells, + 'ins': self.__ins, + 'outs': self.__outs, + 'closure': self.__closure + } diff --git a/src/serialization.py b/src/serialization.py index f72d097..77f251b 100644 --- a/src/serialization.py +++ b/src/serialization.py @@ -7,316 +7,17 @@ Create Date: 2024/3/13 """ -import numpy as np -from shapely.wkt import loads +import json +from indoorspace import IndoorSpace -class Graph: +def serialization(filepath: str, indoorspace: IndoorSpace): + indoorSpace_jsondata = indoorspace.to_json() + with open(filepath, 'w', encoding='utf-8') as file: + json.dump(indoorSpace_jsondata, file, indent=4) - def __init__(self): - self._properties = [] - self._cells = [] - self._connections = [] - self._layers = [] - self._rlineses = [] - self._hypergraph = {} - @property - def properties(self): - return self._properties - - @property - def cells(self): - return self._cells - - @property - def connections(self): - return self._connections - - @property - def layers(self): - return self._layers - - @property - def rlineses(self): - return self._rlineses - - @property - def hypergraph(self): - return self._hypergraph - - class Cell: - - def __init__(self, cell_id, properties, space, node): - self.__id = cell_id - self._properties = properties - self.__space = space - self.__node = node - - @property - def id(self): - return self.__id - - @property - def properties(self): - return self._properties - - @property - def space(self): - return self.__space - - @property - def node(self): - return self.__node - - @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['properties'], - loads(json_data['space']), loads(json_data['node'])) - - def to_json(self): - return { - '$id': self.__id, - 'properties': self._properties, - 'space': self.__space.wkt, - 'node': self.__node.wkt - } - - class Connection: - - def __init__(self, connections_id, properties, source, target, bound, - edge): - self.__id = connections_id - self._properties = properties - self.__source = source - self.__target = target - self.__bound = bound - self.__edge = edge - - @property - def id(self): - return self.__id - - @property - def properties(self): - return self._properties - - @property - def source(self): - return self.__source - - @property - def target(self): - return self.__target - - @property - def bound(self): - return self.__bound - - @property - def edge(self): - return self.__edge - - @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['properties'], - json_data['fr'], json_data['to'], - loads(json_data['bound']), loads(json_data['edge'])) - - def to_json(self): - return { - '$id': self.__id, - 'properties': self._properties, - 'source': self.__source, - 'target': self.__target, - 'bound': self.__bound.wkt, - 'edge': self.__edge.wkt - } - - class Layer: - def __init__(self, layer_id, cells): - self.__id = layer_id - self.__cells = cells - - @property - def id(self): - return self.__id - - @property - def cells(self): - return self.__cells - - @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['cells']) - - def to_json(self): - return { - '$id': self.__id, - 'cells': self.__cells - } - - class Rlines: - def __init__(self, rlines_id, cells, ins, outs, closure): - self.__id = rlines_id - self.__cells = cells - self.__ins = ins - self.__outs = outs - self.__closure = closure - - @property - def id(self): - return self.__id - - @property - def cells(self): - return self.__cells - - @property - def ins(self): - return self.__ins - - @property - def outs(self): - return self.__outs - - @property - def closure(self): - return self.__closure - - @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['cells'], json_data['ins'], json_data['outs'], json_data['closure']) - - def to_json(self): - return { - '$id': self.__id, - 'cells': self.__cells, - 'ins': self.__ins, - 'outs': self.__outs, - 'closure': self.__closure - } - - def set_properties(self, properties: dict): - self._properties.append(properties) - - def add_cell(self, cell: Cell): - if cell.id not in [c.id for c in self._cells]: - self._cells.append(cell) - else: - raise ValueError('Cell id already exists') - - def add_connection(self, connection: Connection): - if connection.id not in [c.id for c in self._connections]: - if connection.source in [ - c.id for c in self._cells - ] and connection.target in [c.id for c in self._cells]: - self._connections.append(connection) - elif connection.source not in [ - c.id for c in self._cells - ] and connection.target in [c.id for c in self._cells]: - raise ValueError('Source cell does not exist') - elif connection.source in [ - c.id for c in self._cells - ] and connection.target not in [c.id for c in self._cells]: - raise ValueError('Target cell does not exist') - else: - raise ValueError('Source and target cell do not exist') - else: - raise ValueError('Connection id already exists') - - def set_layers(self, layers: Layer): - self._layers.append(layers) - - def set_rlineses(self, rlineses: Rlines): - self._rlineses.append(rlineses) - - def get_incident_matrix(self): - cells = self.cells - connections = self.connections - incident_matrix = np.zeros((len(cells), len(connections)), dtype=int) - for j, connections in enumerate(connections): - source = self.get_cell_from_id(connections.source) - target = self.get_cell_from_id(connections.target) - source_index = cells.index(source) - target_index = cells.index(target) - incident_matrix[source_index, j] = 1 - incident_matrix[target_index, j] = -1 - return incident_matrix - - def get_hypergraph(self): - cells = self.cells - connections = self.connections - hypergraph = self._hypergraph - hypergraph['hyperNodes'] = [] - hypergraph['hyperEdges'] = [] - incident_matrix = self.get_incident_matrix() - incident_matrix_transpose = incident_matrix.T - - for hyperNode in connections: - hypergraph['hyperNodes'].append(hyperNode.to_json()) - - for j in range(incident_matrix_transpose.shape[1]): - hyperEdge = {} - inner_edge_id = {"ins": [], "outs": []} - for i in range(incident_matrix_transpose.shape[0]): - if incident_matrix_transpose[i, j] != 0: - if incident_matrix_transpose[i, j] == -1: - inner_edge_ins_id = connections[i].id - inner_edge_id["ins"].append(inner_edge_ins_id) - elif incident_matrix_transpose[i, j] == 1: - inner_edge_outs_id = connections[i].id - inner_edge_id["outs"].append(inner_edge_outs_id) - else: - raise ValueError('Incident matrix error') - hyperEdge['id'] = cells[j].id - hyperEdge['properties'] = cells[j].properties - hyperEdge['space'] = cells[j].space.wkt - hyperEdge['node'] = cells[j].node.wkt - hyperEdge['inner_nodeset'] = inner_edge_id - hypergraph['hyperEdges'].append(hyperEdge) - - self.set_hypergraph(hypergraph) - - return hypergraph - - def set_hypergraph(self, hypergraph): - self._hypergraph = hypergraph - - def get_cell_from_id(self, cell_id): - for cell in self.cells: - if cell.id == cell_id: - return cell - return None - - def get_connection_from_id(self, connection_id): - for connection in self.connections: - if connection.id == connection_id: - return connection - return None - - def to_json(self): - return { - 'properties': self._properties, - 'cells': [cell.to_json() for cell in self._cells], - 'connections': [ - connection.to_json() for connection in self._connections - ], - 'layers': [layer.to_json() for layer in self._layers], - 'rlineses': [rlineses.to_json() for rlineses in self._rlineses] - } - - @staticmethod - def from_json(json_data): - graph = Graph() - graph._properties = json_data['properties'] - graph._cells = [ - Graph.Cell.from_json(cell) for cell in json_data['cells'] - ] - graph._connections = [ - Graph.Connection.from_json(connection) - for connection in json_data['connections'] - ] - graph._layers = json_data['layers'] - graph._rlineses = json_data['rlineses'] - return graph +def deserialization(filepath: str) -> IndoorSpace: + with open(filepath, 'r', encoding='utf-8') as file: + indoorSpace_dict = json.load(file) + return IndoorSpace().from_json(indoorSpace_dict) diff --git a/test/test_create.py b/test/test_create.py index 468421e..e73c1d7 100644 --- a/test/test_create.py +++ b/test/test_create.py @@ -14,9 +14,15 @@ sys.path.append(os.path.abspath('../src')) -from serialization import Graph +from indoorspace import IndoorSpace +from cell import Cell +from connection import Connection +from layer import Layer +from rlines import Rlines +from serialization import serialization -graph = Graph() + +indoorSpace = IndoorSpace() properties = { "name": "indoorjson3-python", @@ -31,25 +37,25 @@ c1_properties = {"roomNumber": "1101"} c1_space = loads("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") c1_node = loads("POINT (0.5 0.5)") -cell1 = graph.Cell(c1_id, c1_properties, c1_space, c1_node) +cell1 = Cell(c1_id, c1_properties, c1_space, c1_node) cells.append(cell1) c2_id = "c2" c2_properties = {"roomNumber": "1102"} c2_space = loads("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))") c2_node = loads("POINT (1.5 0.5)") -cell2 = graph.Cell(c2_id, c2_properties, c2_space, c2_node) +cell2 = Cell(c2_id, c2_properties, c2_space, c2_node) cells.append(cell2) c3_id = "c3" c3_properties = {"roomNumber": "1103"} c3_space = loads("POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))") c3_node = loads("POINT (0.5 1.5)") -cell3 = graph.Cell(c3_id, c3_properties, c3_space, c3_node) +cell3 = Cell(c3_id, c3_properties, c3_space, c3_node) cells.append(cell3) for cell in cells: - graph.add_cell(cell) + indoorSpace.add_cell(cell) connections = [] @@ -59,7 +65,7 @@ con1_to = c2_id con1_bound = loads("LINESTRING (1 0, 1 1)") con1_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") -connection1 = graph.Connection(con1_id, con1_properties, con1_fr, con1_to, con1_bound, con1_edge) +connection1 = Connection(con1_id, con1_properties, con1_fr, con1_to, con1_bound, con1_edge) connections.append(connection1) con2_id = "conn3-1" @@ -68,29 +74,34 @@ con2_to = c1_id con2_bound = loads("LINESTRING (1 0, 1 1)") con2_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") -connection2 = graph.Connection(con2_id, con2_properties, con2_fr, con2_to, con2_bound, con2_edge) +connection2 = Connection(con2_id, con2_properties, con2_fr, con2_to, con2_bound, con2_edge) connections.append(connection2) for connection in connections: - graph.add_connection(connection) + indoorSpace.add_connection(connection) layer_id = "layer" layer_cells = [cell.id for cell in cells] -layer = graph.Layer(layer_id, layer_cells) -graph.set_layers(layer) +layer = Layer(layer_id, layer_cells) +indoorSpace.set_layers(layer) -hypergraph = graph.get_hypergraph() +hypergraph = indoorSpace.get_hypergraph() for hyperEdge in hypergraph['hyperEdges']: i = 0 - if hyperEdge["inner_nodeset"]["ins"] and hyperEdge["inner_nodeset"]["outs"]: - i += 1 - rlines_id = str("rlines") + str(i) - rlines_cell = hyperEdge["id"] - rlines_ins = hyperEdge["inner_nodeset"]["ins"] - rlines_outs = hyperEdge["inner_nodeset"]["outs"] - rlines_closure = [] - rlines = graph.Rlines(rlines_id, rlines_cell, rlines_ins, rlines_outs, rlines_closure) - graph.set_rlineses(rlines) - -with open('test_created.json', 'w', encoding='utf-8') as f: - json.dump(graph.to_json(), f, indent=4) + try: + if hyperEdge["inner_nodeset"]["ins"] and hyperEdge["inner_nodeset"]["outs"]: + i += 1 + rlines_id = str("rlines") + str(i) + rlines_cell = hyperEdge["id"] + rlines_ins = hyperEdge["inner_nodeset"]["ins"] + rlines_outs = hyperEdge["inner_nodeset"]["outs"] + rlines_closure = [] + rlines = Rlines(rlines_id, rlines_cell, rlines_ins, rlines_outs, rlines_closure) + indoorSpace.set_rlineses(rlines) + except KeyError as e: + print(f"KeyError: {e}") + except TypeError as e: + print(f"TypeError: {e}") + +json_file = 'test_created.json' +serialization(json_file, indoorSpace) diff --git a/test/test_created.json b/test/test_created.json index f9edd35..de0dcaf 100644 --- a/test/test_created.json +++ b/test/test_created.json @@ -63,7 +63,7 @@ "rlineses": [ { "$id": "rlines1", - "cells": "c1", + "cell": "c1", "ins": [ "conn3-1" ], diff --git a/test/test_deserialization.py b/test/test_deserialization.py index a569b4f..cb3a5fb 100644 --- a/test/test_deserialization.py +++ b/test/test_deserialization.py @@ -13,13 +13,11 @@ sys.path.append(os.path.abspath('../src')) -from serialization import Graph +from serialization import deserialization -with open('example.json', 'r', encoding='utf-8') as f: - graph_dict = json.load(f) - -graph = Graph().from_json(graph_dict) -hypergraph = graph.get_hypergraph() +json_file = 'example.json' +indoorspace = deserialization(json_file) +hypergraph = indoorspace.get_hypergraph() with open('test_hypergraph.json', 'w', encoding='utf-8') as f: json.dump(hypergraph, f, indent=4) From 36e9684335e839d921f6d6196ad5b3787fb8cb40 Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Fri, 15 Mar 2024 18:23:15 +0800 Subject: [PATCH 05/10] modify the test for seiralization and deserialization, and then simplify the process --- src/cell.py | 69 +++++++--- src/connection.py | 87 ++++++++---- src/indoorspace.py | 88 ++++++------ src/layer.py | 47 +++++-- src/rlines.py | 70 ++++++---- src/serialization.py | 6 +- test/test_create.py | 107 --------------- ...created.json => test_deserialization.json} | 24 +++- test/test_deserialization.py | 29 +++- test/test_hypergraph.json | 72 ---------- test/test_serialization.json | 90 +++++++++++++ test/test_serialization.py | 125 ++++++++++++++++++ 12 files changed, 489 insertions(+), 325 deletions(-) delete mode 100644 test/test_create.py rename test/{test_created.json => test_deserialization.json} (78%) delete mode 100644 test/test_hypergraph.json create mode 100644 test/test_serialization.json create mode 100644 test/test_serialization.py diff --git a/src/cell.py b/src/cell.py index 6fbbf10..6c7c35c 100644 --- a/src/cell.py +++ b/src/cell.py @@ -7,42 +7,69 @@ Create Date: 2024/3/14 """ +import json +from functools import wraps +from typing import Dict from shapely.wkt import loads +from shapely.geometry.base import BaseGeometry + + +def type_check(func): + @wraps(func) + def wrapper(self, cell_id: str, properties: Dict, space: BaseGeometry, node: BaseGeometry, *args, **kwargs): + if not isinstance(cell_id, str): + raise TypeError("cell_id must be a string") + if not isinstance(properties, dict): + raise TypeError("properties must be a dictionary") + if not isinstance(space, BaseGeometry): + raise TypeError("space must be a BaseGeometry instance") + if not isinstance(node, BaseGeometry): + raise TypeError("node must be a BaseGeometry instance") + return func(self, cell_id, properties, space, node, *args, **kwargs) + return wrapper class Cell: - def __init__(self, cell_id, properties, space, node): - self.__id = cell_id - self._properties = properties - self.__space = space - self.__node = node + @type_check + def __init__(self, cell_id: str, properties: Dict, space: BaseGeometry, node: BaseGeometry): + self.__id: str = cell_id + self.__properties: Dict = properties + self.__space: BaseGeometry = space + self.__node: BaseGeometry = node @property - def id(self): + def id(self) -> str: return self.__id @property - def properties(self): - return self._properties + def properties(self) -> Dict: + return self.__properties @property - def space(self): + def space(self) -> BaseGeometry: return self.__space @property - def node(self): + def node(self) -> BaseGeometry: return self.__node + def to_json(self) -> Dict: + result = {} + for key, value in self.__dict__.items(): + json_key = key.split('__')[-1] + if isinstance(value, BaseGeometry): + result[json_key] = value.wkt + elif json_key == 'id': + json_key = '$id' + result[json_key] = value + else: + result[json_key] = value + return result + @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['properties'], - loads(json_data['space']), loads(json_data['node'])) - - def to_json(self): - return { - '$id': self.__id, - 'properties': self._properties, - 'space': self.__space.wkt, - 'node': self.__node.wkt - } + def from_json(cls, json_dict: Dict) -> 'Cell': + json_dict['cell_id'] = json_dict.pop('$id') + json_dict['space'] = loads(json_dict['space']) + json_dict['node'] = loads(json_dict['node']) + return cls(**json_dict) diff --git a/src/connection.py b/src/connection.py index e4cc4c8..9ad15dd 100644 --- a/src/connection.py +++ b/src/connection.py @@ -7,56 +7,85 @@ Create Date: 2024/3/14 """ +import json +from functools import wraps +from typing import Dict from shapely.wkt import loads +from shapely.geometry.base import BaseGeometry + + +def type_check(func): + @wraps(func) + def wrapper(self, connection_id: str, properties: dict, fr: str, to: str, + bound: BaseGeometry, edge: BaseGeometry, *args, **kwargs): + if not isinstance(connection_id, str): + raise TypeError("connection_id must be a string") + if not isinstance(properties, dict): + raise TypeError("properties must be a dictionary") + if not isinstance(fr, str): + raise TypeError("source must be a string") + if not isinstance(to, str): + raise TypeError("target must be a string") + if not isinstance(bound, BaseGeometry): + raise TypeError("bound must be a BaseGeometry instance") + if not isinstance(edge, BaseGeometry): + raise TypeError("edge must be a BaseGeometry instance") + return func(self, connection_id, properties, fr, to, bound, edge, *args, **kwargs) + return wrapper class Connection: - def __init__(self, connections_id, properties, source, target, bound, - edge): - self.__id = connections_id - self._properties = properties - self.__source = source - self.__target = target - self.__bound = bound - self.__edge = edge + @type_check + def __init__(self, connections_id: str, properties: Dict, fr: str, to: str, + bound: BaseGeometry, edge: BaseGeometry): + self.__id: str = connections_id + self.__properties: Dict = properties + self.__source: str = fr + self.__target: str = to + self.__bound: BaseGeometry = bound + self.__edge: BaseGeometry = edge @property - def id(self): + def id(self) -> str: return self.__id @property - def properties(self): - return self._properties + def properties(self) -> dict: + return self.__properties @property - def source(self): + def source(self) -> str: return self.__source @property - def target(self): + def target(self) -> str: return self.__target @property - def bound(self): + def bound(self) -> BaseGeometry: return self.__bound @property - def edge(self): + def edge(self) -> BaseGeometry: return self.__edge + def to_json(self) -> Dict: + result = {} + for key, value in self.__dict__.items(): + json_key = key.split('__')[-1] + if isinstance(value, BaseGeometry): + result[json_key] = value.wkt + elif json_key == 'id': + json_key = '$id' + result[json_key] = value + else: + result[json_key] = value + return result + @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['properties'], - json_data['fr'], json_data['to'], - loads(json_data['bound']), loads(json_data['edge'])) - - def to_json(self): - return { - '$id': self.__id, - 'properties': self._properties, - 'source': self.__source, - 'target': self.__target, - 'bound': self.__bound.wkt, - 'edge': self.__edge.wkt - } + def from_json(cls, json_dict: Dict) -> 'Connection': + json_dict['connection_id'] = json_dict.pop('$id') + json_dict['bound'] = loads(json_dict['bound']) + json_dict['edge'] = loads(json_dict['edge']) + return cls(**json_dict) diff --git a/src/indoorspace.py b/src/indoorspace.py index d31451a..e6bf2c1 100644 --- a/src/indoorspace.py +++ b/src/indoorspace.py @@ -7,49 +7,51 @@ Create Date: 2024/3/14 """ +import json import numpy as np from cell import Cell from connection import Connection from layer import Layer from rlines import Rlines +from typing import List, Dict class IndoorSpace: def __init__(self): - self._properties = [] - self._cells = [] - self._connections = [] - self._layers = [] - self._rlineses = [] - self._hypergraph = {} + self._properties: Dict = {} + self._cells: List[Cell] = [] + self._connections: List[Connection] = [] + self._layers: List[Layer] = [] + self._rlineses: List[Rlines] = [] + self._hypergraph: Dict = {} @property - def properties(self): + def properties(self) -> Dict: return self._properties @property - def cells(self): + def cells(self) -> List[Cell]: return self._cells @property - def connections(self): + def connections(self) -> List[Connection]: return self._connections @property - def layers(self): + def layers(self) -> List[Layer]: return self._layers @property - def rlineses(self): + def rlineses(self) -> List[Rlines]: return self._rlineses @property - def hypergraph(self): + def hypergraph(self) -> Dict: return self._hypergraph - def set_properties(self, properties: dict): - self._properties.append(properties) + def set_properties(self, properties: Dict): + self._properties = properties def add_cell(self, cell: Cell): if cell.id not in [c.id for c in self._cells]: @@ -86,9 +88,9 @@ def get_incident_matrix(self): cells = self.cells connections = self.connections incident_matrix = np.zeros((len(cells), len(connections)), dtype=int) - for j, connections in enumerate(connections): - source = self.get_cell_from_id(connections.source) - target = self.get_cell_from_id(connections.target) + for j, connection in enumerate(connections): + source = self.get_cell_from_id(connection.source) + target = self.get_cell_from_id(connection.target) source_index = cells.index(source) target_index = cells.index(target) incident_matrix[source_index, j] = 1 @@ -149,32 +151,26 @@ def get_connection_from_id(self, connection_id): return connection return None - def to_json(self): - return { - 'properties': self._properties, - 'cells': [cell.to_json() for cell in self._cells], - 'connections': [ - connection.to_json() for connection in self._connections - ], - 'layers': [layer.to_json() for layer in self._layers], - 'rlineses': [rlines.to_json() for rlines in self._rlineses] - } - - @staticmethod - def from_json(json_data): - indoorSpace = IndoorSpace() - - setattr(indoorSpace, '_properties', json_data['properties']) - - class_map = { - '_cells': Cell, - '_connections': Connection, - '_layers': Layer, - '_rlineses': Rlines - } - - for attr, cls in class_map.items(): - items = json_data[attr.lstrip('_')] - setattr(indoorSpace, attr, [cls.from_json(item) for item in items]) - - return indoorSpace + def to_json(self) -> str: + result = {} + for key, value in self.__dict__.items(): + if key == '_hypergraph': + continue + elif key == '_properties': + result[key.strip('_')] = value + else: + result[key.strip('_')] = [item.to_json() for item in value] + return json.dumps(result, indent=4, ensure_ascii=False) + + @classmethod + def from_json(cls, json_str: str) -> 'IndoorSpace': + json_data = json.loads(json_str) + instance = cls() + for key, value in json_data.items(): + if key == 'properties': + setattr(instance, f"_{key}", value) + elif key == 'rlineses': + setattr(instance, f"_{key}", [eval(key.capitalize()[:-2]).from_json(item) for item in value]) + else: + setattr(instance, f"_{key}", [eval(key.capitalize()[:-1]).from_json(item) for item in value]) + return instance diff --git a/src/layer.py b/src/layer.py index 582673d..78d6433 100644 --- a/src/layer.py +++ b/src/layer.py @@ -7,26 +7,47 @@ Create Date: 2024/3/14 """ +import json +from functools import wraps +from typing import List, Dict + + +def type_check(func): + @wraps(func) + def wrapper(self, layer_id: str, cells: List[str], *args, **kwargs): + if not isinstance(layer_id, str): + raise TypeError("layer_id must be a string") + if not isinstance(cells, list): + raise TypeError("cells must be a list") + return func(self, layer_id, cells, *args, **kwargs) + return wrapper + class Layer: - def __init__(self, layer_id, cells): - self.__id = layer_id - self.__cells = cells + + @type_check + def __init__(self, layer_id: str, cells: List[str]): + self.__id: str = layer_id + self.__cells: List[str] = cells @property - def id(self): + def id(self) -> str: return self.__id @property - def cells(self): + def cells(self) -> List[str]: return self.__cells + def to_json(self) -> Dict: + result = {} + for key, value in self.__dict__.items(): + json_key = key.split('__')[-1] + if json_key == 'id': + json_key = '$id' + result[json_key] = value + return result + @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['cells']) - - def to_json(self): - return { - '$id': self.__id, - 'cells': self.__cells - } + def from_json(cls, json_dict: Dict) -> 'Layer': + json_dict['layer_id'] = json_dict.pop('$id') + return cls(**json_dict) diff --git a/src/rlines.py b/src/rlines.py index 1bb9b91..a0be022 100644 --- a/src/rlines.py +++ b/src/rlines.py @@ -7,44 +7,68 @@ Create Date: 2024/3/14 """ +import json +from functools import wraps +from typing import List, Dict + + +def type_check(func): + @wraps(func) + def wrapper(self, rlines_id: str, cell: str, ins: List[str], outs: List[str], closure: List[str], *args, **kwargs): + if not isinstance(rlines_id, str): + raise TypeError("rlines_id must be a string") + if not isinstance(cell, str): + raise TypeError("cells must be a string") + if not isinstance(ins, list): + raise TypeError("ins must be a list") + if not isinstance(outs, list): + raise TypeError("outs must be a list") + if not isinstance(closure, list): + raise TypeError("closure must be a list") + return func(self, rlines_id, cell, ins, outs, closure, *args, **kwargs) + return wrapper + class Rlines: - def __init__(self, rlines_id, cells, ins, outs, closure): - self.__id = rlines_id - self.__cells = cells - self.__ins = ins - self.__outs = outs - self.__closure = closure + + @type_check + def __init__(self, rlines_id: str, cell: str, ins: List[str], outs: List[str], closure: List[str]): + self.__id: str = rlines_id + self.__cell: str = cell + self.__ins: List[str] = ins + self.__outs: List[str] = outs + self.__closure: List[str] = closure @property - def id(self): + def id(self) -> str: return self.__id @property - def cells(self): - return self.__cells + def cell(self) -> str: + return self.__cell @property - def ins(self): + def ins(self) -> List[str]: return self.__ins @property - def outs(self): + def outs(self) -> List[str]: return self.__outs @property - def closure(self): + def closure(self) -> List[str]: return self.__closure + def to_json(self) -> Dict: + result = {} + for key, value in self.__dict__.items(): + json_key = key.split('__')[-1] + if json_key == 'id': + json_key = '$id' + result[json_key] = value + return result + @classmethod - def from_json(cls, json_data): - return cls(json_data['$id'], json_data['cell'], json_data['ins'], json_data['outs'], json_data['closure']) - - def to_json(self): - return { - '$id': self.__id, - 'cell': self.__cells, - 'ins': self.__ins, - 'outs': self.__outs, - 'closure': self.__closure - } + def from_json(cls, json_dict: Dict) -> 'Rlines': + json_dict['rlines_id'] = json_dict.pop('$id') + return cls(**json_dict) diff --git a/src/serialization.py b/src/serialization.py index 77f251b..2f884e9 100644 --- a/src/serialization.py +++ b/src/serialization.py @@ -14,10 +14,10 @@ def serialization(filepath: str, indoorspace: IndoorSpace): indoorSpace_jsondata = indoorspace.to_json() with open(filepath, 'w', encoding='utf-8') as file: - json.dump(indoorSpace_jsondata, file, indent=4) + file.write(indoorSpace_jsondata) def deserialization(filepath: str) -> IndoorSpace: with open(filepath, 'r', encoding='utf-8') as file: - indoorSpace_dict = json.load(file) - return IndoorSpace().from_json(indoorSpace_dict) + indoorSpace_str = file.read() + return IndoorSpace().from_json(indoorSpace_str) diff --git a/test/test_create.py b/test/test_create.py deleted file mode 100644 index e73c1d7..0000000 --- a/test/test_create.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -File Name: test_create.py - -Copyright (c) 2023 - 2024 IndoorJson - -Author: Ziwei Xiang -Create Date: 2024/3/14 -""" - -import json -from shapely.wkt import loads -import sys -import os - -sys.path.append(os.path.abspath('../src')) - -from indoorspace import IndoorSpace -from cell import Cell -from connection import Connection -from layer import Layer -from rlines import Rlines -from serialization import serialization - - -indoorSpace = IndoorSpace() - -properties = { - "name": "indoorjson3-python", - "labels": ["indoorgml", "GIS"], - "language": ["English", "中文", "한국어"], - "author": {"name": "Ziwei Xiang", "email": "knightzz1016@gmail.com"} -} - -cells = [] - -c1_id = "c1" -c1_properties = {"roomNumber": "1101"} -c1_space = loads("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") -c1_node = loads("POINT (0.5 0.5)") -cell1 = Cell(c1_id, c1_properties, c1_space, c1_node) -cells.append(cell1) - -c2_id = "c2" -c2_properties = {"roomNumber": "1102"} -c2_space = loads("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))") -c2_node = loads("POINT (1.5 0.5)") -cell2 = Cell(c2_id, c2_properties, c2_space, c2_node) -cells.append(cell2) - -c3_id = "c3" -c3_properties = {"roomNumber": "1103"} -c3_space = loads("POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))") -c3_node = loads("POINT (0.5 1.5)") -cell3 = Cell(c3_id, c3_properties, c3_space, c3_node) -cells.append(cell3) - -for cell in cells: - indoorSpace.add_cell(cell) - -connections = [] - -con1_id = "conn1-2" -con1_properties = {"type": "door", "开放时间": "全天", "오픈 시간": "하루 종일"} -con1_fr = c1_id -con1_to = c2_id -con1_bound = loads("LINESTRING (1 0, 1 1)") -con1_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") -connection1 = Connection(con1_id, con1_properties, con1_fr, con1_to, con1_bound, con1_edge) -connections.append(connection1) - -con2_id = "conn3-1" -con2_properties = {"type": "window"} -con2_fr = c3_id -con2_to = c1_id -con2_bound = loads("LINESTRING (1 0, 1 1)") -con2_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") -connection2 = Connection(con2_id, con2_properties, con2_fr, con2_to, con2_bound, con2_edge) -connections.append(connection2) - -for connection in connections: - indoorSpace.add_connection(connection) - -layer_id = "layer" -layer_cells = [cell.id for cell in cells] -layer = Layer(layer_id, layer_cells) -indoorSpace.set_layers(layer) - -hypergraph = indoorSpace.get_hypergraph() -for hyperEdge in hypergraph['hyperEdges']: - i = 0 - try: - if hyperEdge["inner_nodeset"]["ins"] and hyperEdge["inner_nodeset"]["outs"]: - i += 1 - rlines_id = str("rlines") + str(i) - rlines_cell = hyperEdge["id"] - rlines_ins = hyperEdge["inner_nodeset"]["ins"] - rlines_outs = hyperEdge["inner_nodeset"]["outs"] - rlines_closure = [] - rlines = Rlines(rlines_id, rlines_cell, rlines_ins, rlines_outs, rlines_closure) - indoorSpace.set_rlineses(rlines) - except KeyError as e: - print(f"KeyError: {e}") - except TypeError as e: - print(f"TypeError: {e}") - -json_file = 'test_created.json' -serialization(json_file, indoorSpace) diff --git a/test/test_created.json b/test/test_deserialization.json similarity index 78% rename from test/test_created.json rename to test/test_deserialization.json index de0dcaf..5b46ef3 100644 --- a/test/test_created.json +++ b/test/test_deserialization.json @@ -1,5 +1,20 @@ { - "properties": [], + "properties": { + "name": "indoorjson3-cpp", + "labels": [ + "indoorgml", + "GIS" + ], + "language": [ + "English", + "中文", + "한국어" + ], + "author": { + "name": "Kunlin Yu", + "email": "yukunlin@syriusrobotics.com" + } + }, "cells": [ { "$id": "c1", @@ -31,8 +46,8 @@ "$id": "conn1-2", "properties": { "type": "door", - "\u5f00\u653e\u65f6\u95f4": "\u5168\u5929", - "\uc624\ud508 \uc2dc\uac04": "\ud558\ub8e8 \uc885\uc77c" + "开放时间": "全天", + "오픈 시간": "하루 종일" }, "source": "c1", "target": "c2", @@ -55,8 +70,7 @@ "$id": "layer", "cells": [ "c1", - "c2", - "c3" + "c2" ] } ], diff --git a/test/test_deserialization.py b/test/test_deserialization.py index cb3a5fb..5ac8519 100644 --- a/test/test_deserialization.py +++ b/test/test_deserialization.py @@ -10,14 +10,31 @@ import json import sys import os +import unittest sys.path.append(os.path.abspath('../src')) -from serialization import deserialization +from serialization import serialization, deserialization -json_file = 'example.json' -indoorspace = deserialization(json_file) -hypergraph = indoorspace.get_hypergraph() -with open('test_hypergraph.json', 'w', encoding='utf-8') as f: - json.dump(hypergraph, f, indent=4) +class TestDeserialization(unittest.TestCase): + + def test_deserialization(self): + + with open('example.json', 'r') as file: + original_json = json.load(file) + + indoorSpace = deserialization('example.json') + + generated_json = 'test_deserialization.json' + + serialization(generated_json, indoorSpace) + + with open(generated_json, 'r') as file: + generated_json = json.load(file) + + self.assertEqual(original_json, generated_json) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_hypergraph.json b/test/test_hypergraph.json deleted file mode 100644 index d122b8b..0000000 --- a/test/test_hypergraph.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "hyperNodes": [ - { - "$id": "conn1-2", - "properties": { - "type": "door", - "\u5f00\u653e\u65f6\u95f4": "\u5168\u5929", - "\uc624\ud508 \uc2dc\uac04": "\ud558\ub8e8 \uc885\uc77c" - }, - "source": "c1", - "target": "c2", - "bound": "LINESTRING (1 0, 1 1)", - "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" - }, - { - "$id": "conn3-1", - "properties": { - "type": "window" - }, - "source": "c3", - "target": "c1", - "bound": "LINESTRING (1 0, 1 1)", - "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" - } - ], - "hyperEdges": [ - { - "id": "c1", - "properties": { - "roomNumber": "1101" - }, - "space": "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", - "node": "POINT (0.5 0.5)", - "inner_nodeset": { - "ins": [ - "conn3-1" - ], - "outs": [ - "conn1-2" - ] - } - }, - { - "id": "c2", - "properties": { - "roomNumber": "1102" - }, - "space": "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", - "node": "POINT (1.5 0.5)", - "inner_nodeset": { - "ins": [ - "conn1-2" - ], - "outs": [] - } - }, - { - "id": "c3", - "properties": { - "roomNumber": "1103" - }, - "space": "POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))", - "node": "POINT (0.5 1.5)", - "inner_nodeset": { - "ins": [], - "outs": [ - "conn3-1" - ] - } - } - ] -} \ No newline at end of file diff --git a/test/test_serialization.json b/test/test_serialization.json new file mode 100644 index 0000000..c82deff --- /dev/null +++ b/test/test_serialization.json @@ -0,0 +1,90 @@ +{ + "properties": { + "name": "indoorjson3-python", + "labels": [ + "indoorgml", + "GIS" + ], + "language": [ + "English", + "中文", + "한국어" + ], + "author": { + "name": "Kunlin Yu", + "email": "yukunlin@syriusrobotics.com" + } + }, + "cells": [ + { + "$id": "c1", + "properties": { + "roomNumber": "1101" + }, + "space": "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", + "node": "POINT (0.5 0.5)" + }, + { + "$id": "c2", + "properties": { + "roomNumber": "1102" + }, + "space": "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", + "node": "POINT (1.5 0.5)" + }, + { + "$id": "c3", + "properties": { + "roomNumber": "1103" + }, + "space": "POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))", + "node": "POINT (0.5 1.5)" + } + ], + "connections": [ + { + "$id": "conn1-2", + "properties": { + "type": "door", + "开放时间": "全天", + "오픈 시간": "하루 종일" + }, + "source": "c1", + "target": "c2", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + }, + { + "$id": "conn3-1", + "properties": { + "type": "window" + }, + "source": "c3", + "target": "c1", + "bound": "LINESTRING (1 0, 1 1)", + "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" + } + ], + "layers": [ + { + "$id": "layer", + "cells": [ + "c1", + "c2" + ] + } + ], + "rlineses": [ + { + "$id": "rlines1", + "cell": "c1", + "ins": [ + "conn3-1" + ], + "outs": [ + "conn1-2" + ], + "closure": [] + } + ] +} \ No newline at end of file diff --git a/test/test_serialization.py b/test/test_serialization.py new file mode 100644 index 0000000..b6dbae4 --- /dev/null +++ b/test/test_serialization.py @@ -0,0 +1,125 @@ +""" +File Name: test_serialization.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/3/14 +""" + +import json +import os +import sys +import unittest + +sys.path.append(os.path.abspath('../src')) + +from shapely.wkt import loads +from indoorspace import IndoorSpace +from cell import Cell +from connection import Connection +from layer import Layer +from rlines import Rlines +from serialization import serialization + + +class TestSerialization(unittest.TestCase): + + def test_serialization(self): + with open('example.json', 'r') as file: + original_json = json.load(file) + + indoorSpace = IndoorSpace() + + properties = { + "name": "indoorjson3-python", + "labels": ["indoorgml", "GIS"], + "language": ["English", "中文", "한국어"], + "author": {"name": "Kunlin Yu", "email": "yukunlin@syriusrobotics.com"} + } + indoorSpace.set_properties(properties) + + cells = [] + + c1_id = "c1" + c1_properties = {"roomNumber": "1101"} + c1_space = loads("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") + c1_node = loads("POINT (0.5 0.5)") + cell1 = Cell(c1_id, c1_properties, c1_space, c1_node) + cells.append(cell1) + + c2_id = "c2" + c2_properties = {"roomNumber": "1102"} + c2_space = loads("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))") + c2_node = loads("POINT (1.5 0.5)") + cell2 = Cell(c2_id, c2_properties, c2_space, c2_node) + cells.append(cell2) + + c3_id = "c3" + c3_properties = {"roomNumber": "1103"} + c3_space = loads("POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))") + c3_node = loads("POINT (0.5 1.5)") + cell3 = Cell(c3_id, c3_properties, c3_space, c3_node) + cells.append(cell3) + + for cell in cells: + indoorSpace.add_cell(cell) + + connections = [] + + con1_id = "conn1-2" + con1_properties = {"type": "door", "开放时间": "全天", "오픈 시간": "하루 종일"} + con1_fr = c1_id + con1_to = c2_id + con1_bound = loads("LINESTRING (1 0, 1 1)") + con1_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") + connection1 = Connection(con1_id, con1_properties, con1_fr, con1_to, con1_bound, con1_edge) + connections.append(connection1) + + con2_id = "conn3-1" + con2_properties = {"type": "window"} + con2_fr = c3_id + con2_to = c1_id + con2_bound = loads("LINESTRING (1 0, 1 1)") + con2_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") + connection2 = Connection(con2_id, con2_properties, con2_fr, con2_to, con2_bound, con2_edge) + connections.append(connection2) + + for connection in connections: + indoorSpace.add_connection(connection) + + layer_id = "layer" + layer_cells = ["c1", "c2"] + layer = Layer(layer_id, layer_cells) + indoorSpace.set_layers(layer) + + hypergraph = indoorSpace.get_hypergraph() + for hyperEdge in hypergraph['hyperEdges']: + i = 0 + try: + if hyperEdge["inner_nodeset"]["ins"] and hyperEdge["inner_nodeset"]["outs"]: + i += 1 + rlines_id = str("rlines") + str(i) + rlines_cell = hyperEdge["id"] + rlines_ins = hyperEdge["inner_nodeset"]["ins"] + rlines_outs = hyperEdge["inner_nodeset"]["outs"] + rlines_closure = [] + rlines = Rlines(rlines_id, rlines_cell, rlines_ins, rlines_outs, rlines_closure) + indoorSpace.set_rlineses(rlines) + except KeyError as e: + print(f"KeyError: {e}") + except TypeError as e: + print(f"TypeError: {e}") + + generated_json = 'test_serialization.json' + + serialization(generated_json, indoorSpace) + + with open(generated_json, 'r') as file: + generated_json = json.load(file) + + self.assertEqual(original_json, generated_json) + + +if __name__ == '__main__': + unittest.main() From 3c5526d33e5bed5a235f47623eb5594f45ea90ba Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Mon, 18 Mar 2024 10:16:11 +0800 Subject: [PATCH 06/10] modify the test and delete some wrong files --- src/connection.py | 8 +-- src/indoorspace.py | 4 +- src/serialization.py | 3 +- test/test_deserialization.py | 7 +- test/test_serialization.json | 90 ------------------------- test/test_serialization.py | 125 ----------------------------------- 6 files changed, 9 insertions(+), 228 deletions(-) delete mode 100644 test/test_serialization.json delete mode 100644 test/test_serialization.py diff --git a/src/connection.py b/src/connection.py index 9ad15dd..d14ab8c 100644 --- a/src/connection.py +++ b/src/connection.py @@ -41,8 +41,8 @@ def __init__(self, connections_id: str, properties: Dict, fr: str, to: str, bound: BaseGeometry, edge: BaseGeometry): self.__id: str = connections_id self.__properties: Dict = properties - self.__source: str = fr - self.__target: str = to + self.__fr: str = fr + self.__to: str = to self.__bound: BaseGeometry = bound self.__edge: BaseGeometry = edge @@ -56,11 +56,11 @@ def properties(self) -> dict: @property def source(self) -> str: - return self.__source + return self.__fr @property def target(self) -> str: - return self.__target + return self.__to @property def bound(self) -> BaseGeometry: diff --git a/src/indoorspace.py b/src/indoorspace.py index e6bf2c1..d0262a2 100644 --- a/src/indoorspace.py +++ b/src/indoorspace.py @@ -151,7 +151,7 @@ def get_connection_from_id(self, connection_id): return connection return None - def to_json(self) -> str: + def to_json(self) -> Dict: result = {} for key, value in self.__dict__.items(): if key == '_hypergraph': @@ -160,7 +160,7 @@ def to_json(self) -> str: result[key.strip('_')] = value else: result[key.strip('_')] = [item.to_json() for item in value] - return json.dumps(result, indent=4, ensure_ascii=False) + return result @classmethod def from_json(cls, json_str: str) -> 'IndoorSpace': diff --git a/src/serialization.py b/src/serialization.py index 2f884e9..dd78f66 100644 --- a/src/serialization.py +++ b/src/serialization.py @@ -12,9 +12,10 @@ def serialization(filepath: str, indoorspace: IndoorSpace): - indoorSpace_jsondata = indoorspace.to_json() + indoorSpace_jsondata = json.dumps(indoorspace.to_json(), indent=4, ensure_ascii=False) with open(filepath, 'w', encoding='utf-8') as file: file.write(indoorSpace_jsondata) + return indoorSpace_jsondata def deserialization(filepath: str) -> IndoorSpace: diff --git a/test/test_deserialization.py b/test/test_deserialization.py index 5ac8519..84da642 100644 --- a/test/test_deserialization.py +++ b/test/test_deserialization.py @@ -26,12 +26,7 @@ def test_deserialization(self): indoorSpace = deserialization('example.json') - generated_json = 'test_deserialization.json' - - serialization(generated_json, indoorSpace) - - with open(generated_json, 'r') as file: - generated_json = json.load(file) + generated_json = indoorSpace.to_json() self.assertEqual(original_json, generated_json) diff --git a/test/test_serialization.json b/test/test_serialization.json deleted file mode 100644 index c82deff..0000000 --- a/test/test_serialization.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "properties": { - "name": "indoorjson3-python", - "labels": [ - "indoorgml", - "GIS" - ], - "language": [ - "English", - "中文", - "한국어" - ], - "author": { - "name": "Kunlin Yu", - "email": "yukunlin@syriusrobotics.com" - } - }, - "cells": [ - { - "$id": "c1", - "properties": { - "roomNumber": "1101" - }, - "space": "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", - "node": "POINT (0.5 0.5)" - }, - { - "$id": "c2", - "properties": { - "roomNumber": "1102" - }, - "space": "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", - "node": "POINT (1.5 0.5)" - }, - { - "$id": "c3", - "properties": { - "roomNumber": "1103" - }, - "space": "POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))", - "node": "POINT (0.5 1.5)" - } - ], - "connections": [ - { - "$id": "conn1-2", - "properties": { - "type": "door", - "开放时间": "全天", - "오픈 시간": "하루 종일" - }, - "source": "c1", - "target": "c2", - "bound": "LINESTRING (1 0, 1 1)", - "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" - }, - { - "$id": "conn3-1", - "properties": { - "type": "window" - }, - "source": "c3", - "target": "c1", - "bound": "LINESTRING (1 0, 1 1)", - "edge": "LINESTRING (0.5 0.5, 1.5 0.5)" - } - ], - "layers": [ - { - "$id": "layer", - "cells": [ - "c1", - "c2" - ] - } - ], - "rlineses": [ - { - "$id": "rlines1", - "cell": "c1", - "ins": [ - "conn3-1" - ], - "outs": [ - "conn1-2" - ], - "closure": [] - } - ] -} \ No newline at end of file diff --git a/test/test_serialization.py b/test/test_serialization.py deleted file mode 100644 index b6dbae4..0000000 --- a/test/test_serialization.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -File Name: test_serialization.py - -Copyright (c) 2023 - 2024 IndoorJson - -Author: Ziwei Xiang -Create Date: 2024/3/14 -""" - -import json -import os -import sys -import unittest - -sys.path.append(os.path.abspath('../src')) - -from shapely.wkt import loads -from indoorspace import IndoorSpace -from cell import Cell -from connection import Connection -from layer import Layer -from rlines import Rlines -from serialization import serialization - - -class TestSerialization(unittest.TestCase): - - def test_serialization(self): - with open('example.json', 'r') as file: - original_json = json.load(file) - - indoorSpace = IndoorSpace() - - properties = { - "name": "indoorjson3-python", - "labels": ["indoorgml", "GIS"], - "language": ["English", "中文", "한국어"], - "author": {"name": "Kunlin Yu", "email": "yukunlin@syriusrobotics.com"} - } - indoorSpace.set_properties(properties) - - cells = [] - - c1_id = "c1" - c1_properties = {"roomNumber": "1101"} - c1_space = loads("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))") - c1_node = loads("POINT (0.5 0.5)") - cell1 = Cell(c1_id, c1_properties, c1_space, c1_node) - cells.append(cell1) - - c2_id = "c2" - c2_properties = {"roomNumber": "1102"} - c2_space = loads("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))") - c2_node = loads("POINT (1.5 0.5)") - cell2 = Cell(c2_id, c2_properties, c2_space, c2_node) - cells.append(cell2) - - c3_id = "c3" - c3_properties = {"roomNumber": "1103"} - c3_space = loads("POLYGON ((0 1, 1 1, 1 2, 0 2, 0 1))") - c3_node = loads("POINT (0.5 1.5)") - cell3 = Cell(c3_id, c3_properties, c3_space, c3_node) - cells.append(cell3) - - for cell in cells: - indoorSpace.add_cell(cell) - - connections = [] - - con1_id = "conn1-2" - con1_properties = {"type": "door", "开放时间": "全天", "오픈 시간": "하루 종일"} - con1_fr = c1_id - con1_to = c2_id - con1_bound = loads("LINESTRING (1 0, 1 1)") - con1_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") - connection1 = Connection(con1_id, con1_properties, con1_fr, con1_to, con1_bound, con1_edge) - connections.append(connection1) - - con2_id = "conn3-1" - con2_properties = {"type": "window"} - con2_fr = c3_id - con2_to = c1_id - con2_bound = loads("LINESTRING (1 0, 1 1)") - con2_edge = loads("LINESTRING (0.5 0.5, 1.5 0.5)") - connection2 = Connection(con2_id, con2_properties, con2_fr, con2_to, con2_bound, con2_edge) - connections.append(connection2) - - for connection in connections: - indoorSpace.add_connection(connection) - - layer_id = "layer" - layer_cells = ["c1", "c2"] - layer = Layer(layer_id, layer_cells) - indoorSpace.set_layers(layer) - - hypergraph = indoorSpace.get_hypergraph() - for hyperEdge in hypergraph['hyperEdges']: - i = 0 - try: - if hyperEdge["inner_nodeset"]["ins"] and hyperEdge["inner_nodeset"]["outs"]: - i += 1 - rlines_id = str("rlines") + str(i) - rlines_cell = hyperEdge["id"] - rlines_ins = hyperEdge["inner_nodeset"]["ins"] - rlines_outs = hyperEdge["inner_nodeset"]["outs"] - rlines_closure = [] - rlines = Rlines(rlines_id, rlines_cell, rlines_ins, rlines_outs, rlines_closure) - indoorSpace.set_rlineses(rlines) - except KeyError as e: - print(f"KeyError: {e}") - except TypeError as e: - print(f"TypeError: {e}") - - generated_json = 'test_serialization.json' - - serialization(generated_json, indoorSpace) - - with open(generated_json, 'r') as file: - generated_json = json.load(file) - - self.assertEqual(original_json, generated_json) - - -if __name__ == '__main__': - unittest.main() From 87a7ccf1a9c04085a99a678fbed206e1c0d466de Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Fri, 19 Apr 2024 10:32:01 +0800 Subject: [PATCH 07/10] add the function of visualization for graph and hypergraph --- src/visualization.py | 92 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/visualization.py diff --git a/src/visualization.py b/src/visualization.py new file mode 100644 index 0000000..040ffdc --- /dev/null +++ b/src/visualization.py @@ -0,0 +1,92 @@ +""" +File Name: visualization.py + +Copyright (c) 2023 - 2024 IndoorJson + +Author: Ziwei Xiang +Create Date: 2024/4/18 +""" + +from indoorspace import IndoorSpace +from shapely import geometry as geo + +import plotly.graph_objs as go +from plotly.offline import plot + + +def graph_visualize(indoorSpace: IndoorSpace): + fig = go.Figure() + + for cell in indoorSpace.cells: + cell_space = geo.Polygon(cell.space) + cell_node = geo.Point(cell.node) + x1, y1 = cell_space.exterior.xy + x2, y2 = cell_node.xy + + fig.add_trace( + go.Scatter(x=list(x1), y=list(y1), fill='toself', name='Space')) + fig.add_trace( + go.Scatter(x=list(x2), + y=list(y2), + mode='markers', + name='Cell', + text=str(cell.properties), + hoverinfo='text')) + + for connection in indoorSpace.connections: + connection_bound = geo.LineString(connection.bound) + connection_edge = geo.LineString(connection.edge) + x1, y1 = connection_bound.xy + x2, y2 = connection_edge.xy + + fig.add_trace( + go.Scatter(x=list(x1), y=list(y1), mode='lines', name='Boundary')) + fig.add_trace( + go.Scatter(x=list(x2), + y=list(y2), + mode='lines', + name='Edge', + text=str(connection.properties), + hoverinfo='text')) + + fig.update_layout(showlegend=False) + + plot(fig, filename='graph.html') + + +def hypergraph_visualize(indoorSpace: IndoorSpace): + fig = go.Figure() + + hypergraph = indoorSpace.get_hypergraph() + + for hyperEdge in hypergraph['hyperEdges']: + cell = indoorSpace.get_cell_from_id(hyperEdge['id']) + connection_dict = hyperEdge['inner_nodeset'] + rlines_group = geo.Polygon(cell.space) + x1, y1 = rlines_group.exterior.xy + + for value in connection_dict.values(): + for connection_id in value: + connection = indoorSpace.get_connection_from_id(connection_id) + connection_point = connection.bound.centroid + x2, y2 = connection_point.xy + + fig.add_trace( + go.Scatter(x=list(x2), + y=list(y2), + mode='markers', + name='Connection Point', + text=str(connection.properties), + hoverinfo='text')) + + fig.add_trace( + go.Scatter(x=list(x1), + y=list(y1), + fill='toself', + name='Rline Group', + text=str(cell.properties), + hoverinfo='text')) + + fig.update_layout(showlegend=False) + + plot(fig, filename='hypergraph.html') From f6924dabd84447e0083c8210cab278f92af83ddd Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Fri, 19 Apr 2024 15:27:34 +0800 Subject: [PATCH 08/10] add the visualization of connectionPoint and rlines --- src/indoorspace.py | 7 +++++++ src/visualization.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/indoorspace.py b/src/indoorspace.py index d0262a2..574debc 100644 --- a/src/indoorspace.py +++ b/src/indoorspace.py @@ -103,6 +103,7 @@ def get_hypergraph_incidence_matrix(self): def get_hypergraph(self): cells = self.cells connections = self.connections + rlineses = self.rlineses hypergraph = self._hypergraph hypergraph['hyperNodes'] = [] hypergraph['hyperEdges'] = [] @@ -130,6 +131,12 @@ def get_hypergraph(self): hyperEdge['space'] = cells[j].space.wkt hyperEdge['node'] = cells[j].node.wkt hyperEdge['inner_nodeset'] = inner_edge_id + + for rlines in rlineses: + if rlines.cell == cells[j].id: + hyperEdge['closure'] = rlines.closure + break + hypergraph['hyperEdges'].append(hyperEdge) self.set_hypergraph(hypergraph) diff --git a/src/visualization.py b/src/visualization.py index 040ffdc..e634de4 100644 --- a/src/visualization.py +++ b/src/visualization.py @@ -63,6 +63,7 @@ def hypergraph_visualize(indoorSpace: IndoorSpace): cell = indoorSpace.get_cell_from_id(hyperEdge['id']) connection_dict = hyperEdge['inner_nodeset'] rlines_group = geo.Polygon(cell.space) + x1, y1 = rlines_group.exterior.xy for value in connection_dict.values(): @@ -79,6 +80,21 @@ def hypergraph_visualize(indoorSpace: IndoorSpace): text=str(connection.properties), hoverinfo='text')) + if 'closure' in hyperEdge: + rlines_closure = hyperEdge['closure'] + for rlines_pairs in rlines_closure: + ins_connection_point = indoorSpace.get_connection_from_id(rlines_pairs[0]).bound.centroid + outs_connection_point = indoorSpace.get_connection_from_id(rlines_pairs[1]).bound.centroid + rline = geo.LineString([ins_connection_point, outs_connection_point]) + x, y = rline.xy + fig.add_trace( + go.Scatter(x=list(x), + y=list(y), + mode='lines', + name='Rline', + text=str(rlines_pairs), + hoverinfo='text')) + fig.add_trace( go.Scatter(x=list(x1), y=list(y1), From bc2be7381cf97a3073eb7b275f181be34b7b3558 Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Sat, 20 Apr 2024 00:56:49 +0800 Subject: [PATCH 09/10] modify the visualization --- src/visualization.py | 108 +++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/src/visualization.py b/src/visualization.py index e634de4..c1f4660 100644 --- a/src/visualization.py +++ b/src/visualization.py @@ -24,11 +24,17 @@ def graph_visualize(indoorSpace: IndoorSpace): x2, y2 = cell_node.xy fig.add_trace( - go.Scatter(x=list(x1), y=list(y1), fill='toself', name='Space')) + go.Scatter(x=list(x1), + y=list(y1), + fill='toself', + fillcolor='#C1DDDB', + line=dict(color='#81B3A9', width=2), + name='Space')) fig.add_trace( go.Scatter(x=list(x2), y=list(y2), mode='markers', + marker=dict(size=10, color='#81B3A9'), name='Cell', text=str(cell.properties), hoverinfo='text')) @@ -40,11 +46,18 @@ def graph_visualize(indoorSpace: IndoorSpace): x2, y2 = connection_edge.xy fig.add_trace( - go.Scatter(x=list(x1), y=list(y1), mode='lines', name='Boundary')) + go.Scatter(x=list(x1), + y=list(y1), + mode='lines', + line=dict(color='#81B3A9', width=2), + name='Boundary', + text=str(connection.properties), + hoverinfo='text')) fig.add_trace( go.Scatter(x=list(x2), y=list(y2), mode='lines', + line=dict(color='#81B3A9', width=2), name='Edge', text=str(connection.properties), hoverinfo='text')) @@ -61,47 +74,82 @@ def hypergraph_visualize(indoorSpace: IndoorSpace): for hyperEdge in hypergraph['hyperEdges']: cell = indoorSpace.get_cell_from_id(hyperEdge['id']) - connection_dict = hyperEdge['inner_nodeset'] + ins = hyperEdge['inner_nodeset']['ins'] + outs = hyperEdge['inner_nodeset']['outs'] + rlines = [] rlines_group = geo.Polygon(cell.space) x1, y1 = rlines_group.exterior.xy - for value in connection_dict.values(): - for connection_id in value: - connection = indoorSpace.get_connection_from_id(connection_id) - connection_point = connection.bound.centroid - x2, y2 = connection_point.xy + fig.add_trace( + go.Scatter(x=list(x1), + y=list(y1), + fill='toself', + fillcolor='#C1DDDB', + line=dict(color='#81B3A9', width=2), + name='Rline Group', + text=str(cell.properties), + hoverinfo='text')) + + for ins_id in ins: + ins_connection_point = indoorSpace.get_connection_from_id( + ins_id).bound.centroid + x2, y2 = ins_connection_point.xy + + for outs_id in outs: + outs_connection_point = indoorSpace.get_connection_from_id( + outs_id).bound.centroid + rline = geo.LineString( + [ins_connection_point, outs_connection_point]) + rlines.append(rline) + + x3, y3 = outs_connection_point.xy fig.add_trace( - go.Scatter(x=list(x2), - y=list(y2), + go.Scatter(x=list(x3), + y=list(y3), mode='markers', + marker=dict(size=10, color='#81B3A9'), name='Connection Point', - text=str(connection.properties), + text=str( + indoorSpace.get_connection_from_id( + outs_id).properties), hoverinfo='text')) + fig.add_trace( + go.Scatter( + x=list(x2), + y=list(y2), + mode='markers', + marker=dict(size=10, color='#81B3A9'), + name='Connection Point', + text=str( + indoorSpace.get_connection_from_id(ins_id).properties), + hoverinfo='text')) + if 'closure' in hyperEdge: rlines_closure = hyperEdge['closure'] for rlines_pairs in rlines_closure: - ins_connection_point = indoorSpace.get_connection_from_id(rlines_pairs[0]).bound.centroid - outs_connection_point = indoorSpace.get_connection_from_id(rlines_pairs[1]).bound.centroid - rline = geo.LineString([ins_connection_point, outs_connection_point]) - x, y = rline.xy - fig.add_trace( - go.Scatter(x=list(x), - y=list(y), - mode='lines', - name='Rline', - text=str(rlines_pairs), - hoverinfo='text')) - - fig.add_trace( - go.Scatter(x=list(x1), - y=list(y1), - fill='toself', - name='Rline Group', - text=str(cell.properties), - hoverinfo='text')) + ins_connection_point = indoorSpace.get_connection_from_id( + rlines_pairs[0]).bound.centroid + outs_connection_point = indoorSpace.get_connection_from_id( + rlines_pairs[1]).bound.centroid + rline_closure = geo.LineString( + [ins_connection_point, outs_connection_point]) + + for rline in rlines: + if rline == rline_closure: + rlines.remove(rline) + break + + for rline in rlines: + x, y = rline.xy + fig.add_trace( + go.Scatter(x=list(x), + y=list(y), + mode='lines', + line=dict(color='#81B3A9'), + name='Rline')) fig.update_layout(showlegend=False) From 3f988862ddcf81788ad6058e094bcd2b5b735cfb Mon Sep 17 00:00:00 2001 From: Knight0132 Date: Mon, 22 Apr 2024 11:18:29 +0800 Subject: [PATCH 10/10] simplify visualization --- src/visualization.py | 77 +++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/src/visualization.py b/src/visualization.py index c1f4660..9051483 100644 --- a/src/visualization.py +++ b/src/visualization.py @@ -7,14 +7,15 @@ Create Date: 2024/4/18 """ -from indoorspace import IndoorSpace -from shapely import geometry as geo - import plotly.graph_objs as go from plotly.offline import plot +from shapely import geometry as geo +from shapely.wkt import loads +from indoorspace import IndoorSpace -def graph_visualize(indoorSpace: IndoorSpace): + +def graph_visualize(indoorSpace: IndoorSpace, filename: str = 'graph.html'): fig = go.Figure() for cell in indoorSpace.cells: @@ -64,10 +65,10 @@ def graph_visualize(indoorSpace: IndoorSpace): fig.update_layout(showlegend=False) - plot(fig, filename='graph.html') + plot(fig, filename=filename) -def hypergraph_visualize(indoorSpace: IndoorSpace): +def hypergraph_visualize(indoorSpace: IndoorSpace, filename: str = 'hypergraph.html'): fig = go.Figure() hypergraph = indoorSpace.get_hypergraph() @@ -79,11 +80,11 @@ def hypergraph_visualize(indoorSpace: IndoorSpace): rlines = [] rlines_group = geo.Polygon(cell.space) - x1, y1 = rlines_group.exterior.xy + x_rlinesGroup, y_rlinesGroup = rlines_group.exterior.xy fig.add_trace( - go.Scatter(x=list(x1), - y=list(y1), + go.Scatter(x=list(x_rlinesGroup), + y=list(y_rlinesGroup), fill='toself', fillcolor='#C1DDDB', line=dict(color='#81B3A9', width=2), @@ -92,50 +93,25 @@ def hypergraph_visualize(indoorSpace: IndoorSpace): hoverinfo='text')) for ins_id in ins: - ins_connection_point = indoorSpace.get_connection_from_id( + insConnectionPoint = indoorSpace.get_connection_from_id( ins_id).bound.centroid - x2, y2 = ins_connection_point.xy for outs_id in outs: - outs_connection_point = indoorSpace.get_connection_from_id( + outsConnectionPoint = indoorSpace.get_connection_from_id( outs_id).bound.centroid rline = geo.LineString( - [ins_connection_point, outs_connection_point]) + [insConnectionPoint, outsConnectionPoint]) rlines.append(rline) - x3, y3 = outs_connection_point.xy - - fig.add_trace( - go.Scatter(x=list(x3), - y=list(y3), - mode='markers', - marker=dict(size=10, color='#81B3A9'), - name='Connection Point', - text=str( - indoorSpace.get_connection_from_id( - outs_id).properties), - hoverinfo='text')) - - fig.add_trace( - go.Scatter( - x=list(x2), - y=list(y2), - mode='markers', - marker=dict(size=10, color='#81B3A9'), - name='Connection Point', - text=str( - indoorSpace.get_connection_from_id(ins_id).properties), - hoverinfo='text')) - if 'closure' in hyperEdge: rlines_closure = hyperEdge['closure'] for rlines_pairs in rlines_closure: - ins_connection_point = indoorSpace.get_connection_from_id( + insConnectionPoint = indoorSpace.get_connection_from_id( rlines_pairs[0]).bound.centroid - outs_connection_point = indoorSpace.get_connection_from_id( + outsConnectionPoint = indoorSpace.get_connection_from_id( rlines_pairs[1]).bound.centroid rline_closure = geo.LineString( - [ins_connection_point, outs_connection_point]) + [insConnectionPoint, outsConnectionPoint]) for rline in rlines: if rline == rline_closure: @@ -143,14 +119,27 @@ def hypergraph_visualize(indoorSpace: IndoorSpace): break for rline in rlines: - x, y = rline.xy + x_rline, y_rline = rline.xy fig.add_trace( - go.Scatter(x=list(x), - y=list(y), + go.Scatter(x=list(x_rline), + y=list(y_rline), mode='lines', line=dict(color='#81B3A9'), name='Rline')) + for hyperNode in hypergraph['hyperNodes']: + connectionPoint = loads(hyperNode['bound']) + x_hyperNode, y_hyperNode = geo.LineString(connectionPoint).centroid.xy + + fig.add_trace( + go.Scatter(x=list(x_hyperNode), + y=list(y_hyperNode), + mode='markers', + marker=dict(size=10, color='#81B3A9'), + name='Connection Point', + text=str(hyperNode['properties']), + hoverinfo='text')) + fig.update_layout(showlegend=False) - plot(fig, filename='hypergraph.html') + plot(fig, filename=filename)