Skip to content

GSP Network API Reference

The GSP Network module provides network-based rendering capabilities, enabling remote visualization and client-server architectures.

Overview

gsp_network

gsp_network provides functionality for rendering graphics over a network using different remote renderers.

Renderer Module

The renderer module contains the network renderer implementation for remote rendering.

gsp_network.renderer

Network renderer that sends rendering requests to a remote server and displays the results using Matplotlib.

NetworkRenderer

Bases: gsp.types.renderer_base.RendererBase

Note: this requires a running gsp_network server. See the README for instructions.

IMPORTANT: it DOES NOT depend on GSP matplotlib renderer, it only uses pip matplotlib to display the remotely rendered images.

Source code in src/gsp_network/renderer/network_renderer.py
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
class NetworkRenderer(RendererBase):
    """**Note**: this requires a running gsp_network server. See the README for instructions.

    **IMPORTANT**: it DOES NOT depend on GSP matplotlib renderer, it only uses pip matplotlib to display the remotely rendered images.
    """

    def __init__(self, canvas: Canvas, server_base_url: str, remote_renderer_name: Literal["matplotlib", "datoviz"] = "matplotlib") -> None:
        """Initialize the NetworkRenderer.

        Args:
            canvas (Canvas): _description_
            server_base_url (str): _description_
            remote_renderer_name (Literal["matplotlib", "datoviz"], optional): _description_. Defaults to "matplotlib".
        """
        self._canvas = canvas
        self._server_base_url = server_base_url
        self._remote_renderer_name: Literal["matplotlib", "datoviz"] = remote_renderer_name

        # Create a figure
        figure_width = self._canvas.get_width() / self._canvas.get_dpi()
        figure_height = self._canvas.get_height() / self._canvas.get_dpi()
        self._figure: matplotlib.figure.Figure = matplotlib.pyplot.figure(figsize=(figure_width, figure_height), dpi=self._canvas.get_dpi())
        assert self._figure.canvas.manager is not None, "matplotlib figure canvas manager is None"
        self._figure.canvas.manager.set_window_title(f"Network ({self._remote_renderer_name})")

        # get the only axes in the figure
        self._mpl_axes = self._figure.add_axes((0, 0, 1, 1))
        # hide the borders
        self._mpl_axes.axis("off")

        # create an np.array to hold the image
        image_data_np = np.zeros((self._canvas.get_height(), self._canvas.get_width(), 3), dtype=np.uint8)
        self._axes_image = self._mpl_axes.imshow(image_data_np, aspect="auto")

    def get_canvas(self) -> Canvas:
        """Get the canvas associated with the network renderer.

        Returns:
            Canvas: The canvas associated with the network renderer.
        """
        return self._canvas

    def close(self) -> None:
        """Close the network renderer and release resources."""
        # stop the event loop if any - thus .show(block=True) will return
        self._figure.canvas.stop_event_loop()
        # close the figure
        matplotlib.pyplot.close(self._figure)
        self._figure = None  # type: ignore

    def get_remote_renderer_name(self) -> Literal["matplotlib", "datoviz"]:
        """Get the name of the remote renderer being used.

        Returns:
            Literal["matplotlib", "datoviz"]: The name of the remote renderer.
        """
        return self._remote_renderer_name

    def render(
        self,
        viewports: Sequence[Viewport],
        visuals: Sequence[VisualBase],
        model_matrices: Sequence[TransBuf],
        cameras: Sequence[Camera],
    ) -> bytes:
        """Render the scene remotely and update the matplotlib figure with the rendered image.

        Args:
            viewports (Sequence[Viewport]): The viewports to render.
            visuals (Sequence[VisualBase]): The visuals to render.
            model_matrices (Sequence[TransBuf]): The model matrices for the visuals.
            cameras (Sequence[Camera]): The cameras to use for rendering.

        Returns:
            bytes: The rendered image data in PNG format.

        Raises:
            Exception: If the network request fails.
        """
        # =============================================================================
        # Serialize the scene and create the payload
        # =============================================================================
        pydanticSerializer = PydanticSerializer(self._canvas)
        pydantic_scene_dict = pydanticSerializer.serialize(
            viewports=viewports,
            visuals=visuals,
            model_matrices=model_matrices,
            cameras=cameras,
        )

        payload: NetworkPayload = {
            "renderer_name": self._remote_renderer_name,
            "data": pydantic_scene_dict,
        }

        # =============================================================================
        # do network request to send the payload and get the rendered image
        # =============================================================================
        # Send the POST request with JSON data
        call_url = f"{self._server_base_url}/render"
        headers = {"Content-Type": "application/json"}
        response = requests.post(call_url, data=json.dumps(payload), headers=headers)

        # Check the response status
        if response.status_code != HttpStatus.OK:
            raise Exception(f"Request failed with status code {response.status_code}")
        image_png_data = response.content

        # =============================================================================
        # Render the image in the matplotlib figure
        # =============================================================================
        assert self._axes_image is not None, "PANIC self._axes_image is None"
        # update the image data
        image_data_io = io.BytesIO(image_png_data)
        image_data_np = matplotlib.image.imread(image_data_io, format="png")
        self._axes_image.set_data(image_data_np)

        # return png data as bytes
        return image_png_data

    def show(self) -> None:
        """Show the rendered canvas (blocking call)."""
        # handle non-interactive mode for tests
        in_test = os.environ.get("GSP_TEST") == "True"
        if in_test:
            return

        matplotlib.pyplot.show()

    def get_mpl_figure(self) -> matplotlib.figure.Figure:
        """Get the underlying Matplotlib figure.

        Returns:
            matplotlib.figure.Figure: The Matplotlib figure used by the renderer.
        """
        return self._figure

__init__(canvas: Canvas, server_base_url: str, remote_renderer_name: Literal['matplotlib', 'datoviz'] = 'matplotlib') -> None

Initialize the NetworkRenderer.

Parameters:

Name Type Description Default
canvas gsp.core.canvas.Canvas

description

required
server_base_url str

description

required
remote_renderer_name typing.Literal['matplotlib', 'datoviz']

description. Defaults to "matplotlib".

'matplotlib'
Source code in src/gsp_network/renderer/network_renderer.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def __init__(self, canvas: Canvas, server_base_url: str, remote_renderer_name: Literal["matplotlib", "datoviz"] = "matplotlib") -> None:
    """Initialize the NetworkRenderer.

    Args:
        canvas (Canvas): _description_
        server_base_url (str): _description_
        remote_renderer_name (Literal["matplotlib", "datoviz"], optional): _description_. Defaults to "matplotlib".
    """
    self._canvas = canvas
    self._server_base_url = server_base_url
    self._remote_renderer_name: Literal["matplotlib", "datoviz"] = remote_renderer_name

    # Create a figure
    figure_width = self._canvas.get_width() / self._canvas.get_dpi()
    figure_height = self._canvas.get_height() / self._canvas.get_dpi()
    self._figure: matplotlib.figure.Figure = matplotlib.pyplot.figure(figsize=(figure_width, figure_height), dpi=self._canvas.get_dpi())
    assert self._figure.canvas.manager is not None, "matplotlib figure canvas manager is None"
    self._figure.canvas.manager.set_window_title(f"Network ({self._remote_renderer_name})")

    # get the only axes in the figure
    self._mpl_axes = self._figure.add_axes((0, 0, 1, 1))
    # hide the borders
    self._mpl_axes.axis("off")

    # create an np.array to hold the image
    image_data_np = np.zeros((self._canvas.get_height(), self._canvas.get_width(), 3), dtype=np.uint8)
    self._axes_image = self._mpl_axes.imshow(image_data_np, aspect="auto")

get_canvas() -> Canvas

Get the canvas associated with the network renderer.

Returns:

Name Type Description
Canvas gsp.core.canvas.Canvas

The canvas associated with the network renderer.

Source code in src/gsp_network/renderer/network_renderer.py
71
72
73
74
75
76
77
def get_canvas(self) -> Canvas:
    """Get the canvas associated with the network renderer.

    Returns:
        Canvas: The canvas associated with the network renderer.
    """
    return self._canvas

close() -> None

Close the network renderer and release resources.

Source code in src/gsp_network/renderer/network_renderer.py
79
80
81
82
83
84
85
def close(self) -> None:
    """Close the network renderer and release resources."""
    # stop the event loop if any - thus .show(block=True) will return
    self._figure.canvas.stop_event_loop()
    # close the figure
    matplotlib.pyplot.close(self._figure)
    self._figure = None  # type: ignore

get_remote_renderer_name() -> Literal['matplotlib', 'datoviz']

Get the name of the remote renderer being used.

Returns:

Type Description
typing.Literal['matplotlib', 'datoviz']

Literal["matplotlib", "datoviz"]: The name of the remote renderer.

Source code in src/gsp_network/renderer/network_renderer.py
87
88
89
90
91
92
93
def get_remote_renderer_name(self) -> Literal["matplotlib", "datoviz"]:
    """Get the name of the remote renderer being used.

    Returns:
        Literal["matplotlib", "datoviz"]: The name of the remote renderer.
    """
    return self._remote_renderer_name

render(viewports: Sequence[Viewport], visuals: Sequence[VisualBase], model_matrices: Sequence[TransBuf], cameras: Sequence[Camera]) -> bytes

Render the scene remotely and update the matplotlib figure with the rendered image.

Parameters:

Name Type Description Default
viewports typing.Sequence[gsp.core.viewport.Viewport]

The viewports to render.

required
visuals typing.Sequence[gsp.types.visual_base.VisualBase]

The visuals to render.

required
model_matrices typing.Sequence[gsp.types.transbuf.TransBuf]

The model matrices for the visuals.

required
cameras typing.Sequence[gsp.core.camera.Camera]

The cameras to use for rendering.

required

Returns:

Name Type Description
bytes bytes

The rendered image data in PNG format.

Raises:

Type Description
Exception

If the network request fails.

Source code in src/gsp_network/renderer/network_renderer.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def render(
    self,
    viewports: Sequence[Viewport],
    visuals: Sequence[VisualBase],
    model_matrices: Sequence[TransBuf],
    cameras: Sequence[Camera],
) -> bytes:
    """Render the scene remotely and update the matplotlib figure with the rendered image.

    Args:
        viewports (Sequence[Viewport]): The viewports to render.
        visuals (Sequence[VisualBase]): The visuals to render.
        model_matrices (Sequence[TransBuf]): The model matrices for the visuals.
        cameras (Sequence[Camera]): The cameras to use for rendering.

    Returns:
        bytes: The rendered image data in PNG format.

    Raises:
        Exception: If the network request fails.
    """
    # =============================================================================
    # Serialize the scene and create the payload
    # =============================================================================
    pydanticSerializer = PydanticSerializer(self._canvas)
    pydantic_scene_dict = pydanticSerializer.serialize(
        viewports=viewports,
        visuals=visuals,
        model_matrices=model_matrices,
        cameras=cameras,
    )

    payload: NetworkPayload = {
        "renderer_name": self._remote_renderer_name,
        "data": pydantic_scene_dict,
    }

    # =============================================================================
    # do network request to send the payload and get the rendered image
    # =============================================================================
    # Send the POST request with JSON data
    call_url = f"{self._server_base_url}/render"
    headers = {"Content-Type": "application/json"}
    response = requests.post(call_url, data=json.dumps(payload), headers=headers)

    # Check the response status
    if response.status_code != HttpStatus.OK:
        raise Exception(f"Request failed with status code {response.status_code}")
    image_png_data = response.content

    # =============================================================================
    # Render the image in the matplotlib figure
    # =============================================================================
    assert self._axes_image is not None, "PANIC self._axes_image is None"
    # update the image data
    image_data_io = io.BytesIO(image_png_data)
    image_data_np = matplotlib.image.imread(image_data_io, format="png")
    self._axes_image.set_data(image_data_np)

    # return png data as bytes
    return image_png_data

show() -> None

Show the rendered canvas (blocking call).

Source code in src/gsp_network/renderer/network_renderer.py
157
158
159
160
161
162
163
164
def show(self) -> None:
    """Show the rendered canvas (blocking call)."""
    # handle non-interactive mode for tests
    in_test = os.environ.get("GSP_TEST") == "True"
    if in_test:
        return

    matplotlib.pyplot.show()

get_mpl_figure() -> matplotlib.figure.Figure

Get the underlying Matplotlib figure.

Returns:

Type Description
matplotlib.figure.Figure

matplotlib.figure.Figure: The Matplotlib figure used by the renderer.

Source code in src/gsp_network/renderer/network_renderer.py
166
167
168
169
170
171
172
def get_mpl_figure(self) -> matplotlib.figure.Figure:
    """Get the underlying Matplotlib figure.

    Returns:
        matplotlib.figure.Figure: The Matplotlib figure used by the renderer.
    """
    return self._figure

Network Renderer

gsp_network.renderer.network_renderer

Network renderer that sends rendering requests to a remote server and displays the results using Matplotlib.

NetworkPayload

Bases: typing.TypedDict

Type definition for the network payload sent to the server.

Source code in src/gsp_network/renderer/network_renderer.py
31
32
33
34
class NetworkPayload(TypedDict):
    """Type definition for the network payload sent to the server."""
    renderer_name: Literal["matplotlib", "datoviz"]
    data: PydanticDict

NetworkRenderer

Bases: gsp.types.renderer_base.RendererBase

Note: this requires a running gsp_network server. See the README for instructions.

IMPORTANT: it DOES NOT depend on GSP matplotlib renderer, it only uses pip matplotlib to display the remotely rendered images.

Source code in src/gsp_network/renderer/network_renderer.py
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
class NetworkRenderer(RendererBase):
    """**Note**: this requires a running gsp_network server. See the README for instructions.

    **IMPORTANT**: it DOES NOT depend on GSP matplotlib renderer, it only uses pip matplotlib to display the remotely rendered images.
    """

    def __init__(self, canvas: Canvas, server_base_url: str, remote_renderer_name: Literal["matplotlib", "datoviz"] = "matplotlib") -> None:
        """Initialize the NetworkRenderer.

        Args:
            canvas (Canvas): _description_
            server_base_url (str): _description_
            remote_renderer_name (Literal["matplotlib", "datoviz"], optional): _description_. Defaults to "matplotlib".
        """
        self._canvas = canvas
        self._server_base_url = server_base_url
        self._remote_renderer_name: Literal["matplotlib", "datoviz"] = remote_renderer_name

        # Create a figure
        figure_width = self._canvas.get_width() / self._canvas.get_dpi()
        figure_height = self._canvas.get_height() / self._canvas.get_dpi()
        self._figure: matplotlib.figure.Figure = matplotlib.pyplot.figure(figsize=(figure_width, figure_height), dpi=self._canvas.get_dpi())
        assert self._figure.canvas.manager is not None, "matplotlib figure canvas manager is None"
        self._figure.canvas.manager.set_window_title(f"Network ({self._remote_renderer_name})")

        # get the only axes in the figure
        self._mpl_axes = self._figure.add_axes((0, 0, 1, 1))
        # hide the borders
        self._mpl_axes.axis("off")

        # create an np.array to hold the image
        image_data_np = np.zeros((self._canvas.get_height(), self._canvas.get_width(), 3), dtype=np.uint8)
        self._axes_image = self._mpl_axes.imshow(image_data_np, aspect="auto")

    def get_canvas(self) -> Canvas:
        """Get the canvas associated with the network renderer.

        Returns:
            Canvas: The canvas associated with the network renderer.
        """
        return self._canvas

    def close(self) -> None:
        """Close the network renderer and release resources."""
        # stop the event loop if any - thus .show(block=True) will return
        self._figure.canvas.stop_event_loop()
        # close the figure
        matplotlib.pyplot.close(self._figure)
        self._figure = None  # type: ignore

    def get_remote_renderer_name(self) -> Literal["matplotlib", "datoviz"]:
        """Get the name of the remote renderer being used.

        Returns:
            Literal["matplotlib", "datoviz"]: The name of the remote renderer.
        """
        return self._remote_renderer_name

    def render(
        self,
        viewports: Sequence[Viewport],
        visuals: Sequence[VisualBase],
        model_matrices: Sequence[TransBuf],
        cameras: Sequence[Camera],
    ) -> bytes:
        """Render the scene remotely and update the matplotlib figure with the rendered image.

        Args:
            viewports (Sequence[Viewport]): The viewports to render.
            visuals (Sequence[VisualBase]): The visuals to render.
            model_matrices (Sequence[TransBuf]): The model matrices for the visuals.
            cameras (Sequence[Camera]): The cameras to use for rendering.

        Returns:
            bytes: The rendered image data in PNG format.

        Raises:
            Exception: If the network request fails.
        """
        # =============================================================================
        # Serialize the scene and create the payload
        # =============================================================================
        pydanticSerializer = PydanticSerializer(self._canvas)
        pydantic_scene_dict = pydanticSerializer.serialize(
            viewports=viewports,
            visuals=visuals,
            model_matrices=model_matrices,
            cameras=cameras,
        )

        payload: NetworkPayload = {
            "renderer_name": self._remote_renderer_name,
            "data": pydantic_scene_dict,
        }

        # =============================================================================
        # do network request to send the payload and get the rendered image
        # =============================================================================
        # Send the POST request with JSON data
        call_url = f"{self._server_base_url}/render"
        headers = {"Content-Type": "application/json"}
        response = requests.post(call_url, data=json.dumps(payload), headers=headers)

        # Check the response status
        if response.status_code != HttpStatus.OK:
            raise Exception(f"Request failed with status code {response.status_code}")
        image_png_data = response.content

        # =============================================================================
        # Render the image in the matplotlib figure
        # =============================================================================
        assert self._axes_image is not None, "PANIC self._axes_image is None"
        # update the image data
        image_data_io = io.BytesIO(image_png_data)
        image_data_np = matplotlib.image.imread(image_data_io, format="png")
        self._axes_image.set_data(image_data_np)

        # return png data as bytes
        return image_png_data

    def show(self) -> None:
        """Show the rendered canvas (blocking call)."""
        # handle non-interactive mode for tests
        in_test = os.environ.get("GSP_TEST") == "True"
        if in_test:
            return

        matplotlib.pyplot.show()

    def get_mpl_figure(self) -> matplotlib.figure.Figure:
        """Get the underlying Matplotlib figure.

        Returns:
            matplotlib.figure.Figure: The Matplotlib figure used by the renderer.
        """
        return self._figure

__init__(canvas: Canvas, server_base_url: str, remote_renderer_name: Literal['matplotlib', 'datoviz'] = 'matplotlib') -> None

Initialize the NetworkRenderer.

Parameters:

Name Type Description Default
canvas gsp.core.canvas.Canvas

description

required
server_base_url str

description

required
remote_renderer_name typing.Literal['matplotlib', 'datoviz']

description. Defaults to "matplotlib".

'matplotlib'
Source code in src/gsp_network/renderer/network_renderer.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def __init__(self, canvas: Canvas, server_base_url: str, remote_renderer_name: Literal["matplotlib", "datoviz"] = "matplotlib") -> None:
    """Initialize the NetworkRenderer.

    Args:
        canvas (Canvas): _description_
        server_base_url (str): _description_
        remote_renderer_name (Literal["matplotlib", "datoviz"], optional): _description_. Defaults to "matplotlib".
    """
    self._canvas = canvas
    self._server_base_url = server_base_url
    self._remote_renderer_name: Literal["matplotlib", "datoviz"] = remote_renderer_name

    # Create a figure
    figure_width = self._canvas.get_width() / self._canvas.get_dpi()
    figure_height = self._canvas.get_height() / self._canvas.get_dpi()
    self._figure: matplotlib.figure.Figure = matplotlib.pyplot.figure(figsize=(figure_width, figure_height), dpi=self._canvas.get_dpi())
    assert self._figure.canvas.manager is not None, "matplotlib figure canvas manager is None"
    self._figure.canvas.manager.set_window_title(f"Network ({self._remote_renderer_name})")

    # get the only axes in the figure
    self._mpl_axes = self._figure.add_axes((0, 0, 1, 1))
    # hide the borders
    self._mpl_axes.axis("off")

    # create an np.array to hold the image
    image_data_np = np.zeros((self._canvas.get_height(), self._canvas.get_width(), 3), dtype=np.uint8)
    self._axes_image = self._mpl_axes.imshow(image_data_np, aspect="auto")

close() -> None

Close the network renderer and release resources.

Source code in src/gsp_network/renderer/network_renderer.py
79
80
81
82
83
84
85
def close(self) -> None:
    """Close the network renderer and release resources."""
    # stop the event loop if any - thus .show(block=True) will return
    self._figure.canvas.stop_event_loop()
    # close the figure
    matplotlib.pyplot.close(self._figure)
    self._figure = None  # type: ignore

get_canvas() -> Canvas

Get the canvas associated with the network renderer.

Returns:

Name Type Description
Canvas gsp.core.canvas.Canvas

The canvas associated with the network renderer.

Source code in src/gsp_network/renderer/network_renderer.py
71
72
73
74
75
76
77
def get_canvas(self) -> Canvas:
    """Get the canvas associated with the network renderer.

    Returns:
        Canvas: The canvas associated with the network renderer.
    """
    return self._canvas

get_mpl_figure() -> matplotlib.figure.Figure

Get the underlying Matplotlib figure.

Returns:

Type Description
matplotlib.figure.Figure

matplotlib.figure.Figure: The Matplotlib figure used by the renderer.

Source code in src/gsp_network/renderer/network_renderer.py
166
167
168
169
170
171
172
def get_mpl_figure(self) -> matplotlib.figure.Figure:
    """Get the underlying Matplotlib figure.

    Returns:
        matplotlib.figure.Figure: The Matplotlib figure used by the renderer.
    """
    return self._figure

get_remote_renderer_name() -> Literal['matplotlib', 'datoviz']

Get the name of the remote renderer being used.

Returns:

Type Description
typing.Literal['matplotlib', 'datoviz']

Literal["matplotlib", "datoviz"]: The name of the remote renderer.

Source code in src/gsp_network/renderer/network_renderer.py
87
88
89
90
91
92
93
def get_remote_renderer_name(self) -> Literal["matplotlib", "datoviz"]:
    """Get the name of the remote renderer being used.

    Returns:
        Literal["matplotlib", "datoviz"]: The name of the remote renderer.
    """
    return self._remote_renderer_name

render(viewports: Sequence[Viewport], visuals: Sequence[VisualBase], model_matrices: Sequence[TransBuf], cameras: Sequence[Camera]) -> bytes

Render the scene remotely and update the matplotlib figure with the rendered image.

Parameters:

Name Type Description Default
viewports typing.Sequence[gsp.core.viewport.Viewport]

The viewports to render.

required
visuals typing.Sequence[gsp.types.visual_base.VisualBase]

The visuals to render.

required
model_matrices typing.Sequence[gsp.types.transbuf.TransBuf]

The model matrices for the visuals.

required
cameras typing.Sequence[gsp.core.camera.Camera]

The cameras to use for rendering.

required

Returns:

Name Type Description
bytes bytes

The rendered image data in PNG format.

Raises:

Type Description
Exception

If the network request fails.

Source code in src/gsp_network/renderer/network_renderer.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def render(
    self,
    viewports: Sequence[Viewport],
    visuals: Sequence[VisualBase],
    model_matrices: Sequence[TransBuf],
    cameras: Sequence[Camera],
) -> bytes:
    """Render the scene remotely and update the matplotlib figure with the rendered image.

    Args:
        viewports (Sequence[Viewport]): The viewports to render.
        visuals (Sequence[VisualBase]): The visuals to render.
        model_matrices (Sequence[TransBuf]): The model matrices for the visuals.
        cameras (Sequence[Camera]): The cameras to use for rendering.

    Returns:
        bytes: The rendered image data in PNG format.

    Raises:
        Exception: If the network request fails.
    """
    # =============================================================================
    # Serialize the scene and create the payload
    # =============================================================================
    pydanticSerializer = PydanticSerializer(self._canvas)
    pydantic_scene_dict = pydanticSerializer.serialize(
        viewports=viewports,
        visuals=visuals,
        model_matrices=model_matrices,
        cameras=cameras,
    )

    payload: NetworkPayload = {
        "renderer_name": self._remote_renderer_name,
        "data": pydantic_scene_dict,
    }

    # =============================================================================
    # do network request to send the payload and get the rendered image
    # =============================================================================
    # Send the POST request with JSON data
    call_url = f"{self._server_base_url}/render"
    headers = {"Content-Type": "application/json"}
    response = requests.post(call_url, data=json.dumps(payload), headers=headers)

    # Check the response status
    if response.status_code != HttpStatus.OK:
        raise Exception(f"Request failed with status code {response.status_code}")
    image_png_data = response.content

    # =============================================================================
    # Render the image in the matplotlib figure
    # =============================================================================
    assert self._axes_image is not None, "PANIC self._axes_image is None"
    # update the image data
    image_data_io = io.BytesIO(image_png_data)
    image_data_np = matplotlib.image.imread(image_data_io, format="png")
    self._axes_image.set_data(image_data_np)

    # return png data as bytes
    return image_png_data

show() -> None

Show the rendered canvas (blocking call).

Source code in src/gsp_network/renderer/network_renderer.py
157
158
159
160
161
162
163
164
def show(self) -> None:
    """Show the rendered canvas (blocking call)."""
    # handle non-interactive mode for tests
    in_test = os.environ.get("GSP_TEST") == "True"
    if in_test:
        return

    matplotlib.pyplot.show()

Tools Module

The tools module provides server utilities for network-based rendering.

gsp_network.tools

tools package initialization.

Network Server

gsp_network.tools.network_server

Server example using Flask to render a scene from JSON input.

  • use Flask to create a simple web server
  • render with matplotlib or datoviz based on environment variable

ServerSample

Sample class to demonstrate server functionality.

Source code in src/gsp_network/tools/network_server.py
 92
 93
 94
 95
 96
 97
 98
 99
100
class ServerSample:
    """Sample class to demonstrate server functionality."""
    def __init__(self):
        """Initialize the server sample."""
        pass

    def run(self):
        """Run the Flask server."""
        flask_app.run(threaded=False, debug=False)  # Enable debug mode if desired

__init__()

Initialize the server sample.

Source code in src/gsp_network/tools/network_server.py
94
95
96
def __init__(self):
    """Initialize the server sample."""
    pass

run()

Run the Flask server.

Source code in src/gsp_network/tools/network_server.py
 98
 99
100
def run(self):
    """Run the Flask server."""
    flask_app.run(threaded=False, debug=False)  # Enable debug mode if desired

render_scene_json() -> Response

Flask route to render a scene from JSON input.

Returns:

Name Type Description
Response flask.Response

Flask response containing the rendered PNG image.

Source code in src/gsp_network/tools/network_server.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@flask_app.route("/render", methods=["POST"])
def render_scene_json() -> Response:
    """Flask route to render a scene from JSON input.

    Returns:
        Response: Flask response containing the rendered PNG image.
    """
    payload: NetworkPayload = request.get_json()

    # Log the received payload for debugging
    print("Received payload")

    ###############################################################################
    # Load the scene from JSON
    #

    pydanticDict: PydanticDict = payload["data"]

    pydanticParser = PydanticParser()
    parsed_canvas, parsed_viewports, parsed_visuals, parsed_model_matrices, parsed_cameras = pydanticParser.parse(pydanticDict)

    ###############################################################################
    # Render the loaded scene with matplotlib or datoviz based on environment variable
    #
    renderer_name = payload["renderer_name"]
    if renderer_name == "matplotlib":
        renderer = MatplotlibRenderer(parsed_canvas)
    else:
        renderer = DatovizRenderer(parsed_canvas, offscreen=True)
    image_png_data = renderer.render(parsed_viewports, parsed_visuals, parsed_model_matrices, parsed_cameras, return_image=True)

    print(f"Rendered image size: {text_cyan(str(len(image_png_data)))} bytes")

    ###############################################################################
    # Return the rendered image as a PNG file
    #
    return send_file(
        io.BytesIO(image_png_data),
        mimetype="image/png",
        as_attachment=True,
        download_name="rendered_scene.png",
    )

text_cyan(text: str) -> str

Return the given text string wrapped in ANSI escape codes for cyan color.

Parameters:

Name Type Description Default
text str

The text to color.

required

Returns:

Name Type Description
str str

The colored text string.

Source code in src/gsp_network/tools/network_server.py
28
29
30
31
32
33
34
35
36
37
def text_cyan(text: str) -> str:
    """Return the given text string wrapped in ANSI escape codes for cyan color.

    Args:
        text (str): The text to color.

    Returns:
        str: The colored text string.
    """
    return colorama.Fore.CYAN + text + colorama.Style.RESET_ALL

Network Server Kill

gsp_network.tools.network_server_kill

Kill any process using port 5000 (commonly used for flask server).

in shell: lsof -ti tcp:5000 | xargs kill

main() -> int

Main function to kill processes using port 5000.

Returns:

Name Type Description
int int

Exit code (0 for success, 1 for failure).

Source code in src/gsp_network/tools/network_server_kill.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def main() -> int:
    """Main function to kill processes using port 5000.

    Returns:
        int: Exit code (0 for success, 1 for failure).
    """
    # parse command line arguments
    parser = argparse.ArgumentParser(description="Kill any process using port 5000 (commonly used for flask server).")
    _ = parser.parse_args()

    port = 5000

    try:
        # Get the list of process IDs using the specified port
        result = subprocess.run(["lsof", "-ti", f"tcp:{port}"], capture_output=True, text=True)
        pids = result.stdout.strip().split("\n")

        if pids == [""]:
            print(f"No processes found using port {port}.")
            return 0

        for pid in pids:
            os.kill(int(pid), signal.SIGTERM)
            print(f"Killed process with PID: {pid} using port {port}.")

        return 0

    except Exception as error:
        print(f"An error occurred: {error}")
        return 1