heremaps/pptk

Connecting to Existing Viewer

mwil opened this issue · 3 comments

mwil commented

I would like to connect to an already existing viewer (for example by providing its port) and use it the same way as pptk.viewer(points). While developing I have to restart the whole script, so at the moment I'm pointlessly loading the point cloud again and again.

I'm probably late to help you, but i'll share the solution anyway.

You need to connect to the socket server port displayed at the pptk viewer's top-right corner. This port was created by the QTCPSocket and you can connect to it via python, for example, using:

import socket
portNumber = <insert server port number here>
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', portNumber)).

From now on you need to define a message to send to this port. You may check the function def __send(self, msg) in $ROOT/pptk/pptk/viewer/viewer.py and its calls to understand how it works. I'll append a sample code based on existing functions of viewer.py:

#!/bin/python3.8
import struct
import socket
import numpy

def __load(positions):
    numPoints = int(positions.size /3)
    msg = struct.pack('b',1) + struct.pack('i',numPoints) + positions.tobytes()
    __send(msg)

def __send(msg):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('localhost', portNumber))
    totalSent = 0
    while totalSent < len(msg):
        sent = s.send(msg)
        if sent == 0:
            raise RuntimeError("socket connection broken")
        totalSent = totalSent + sent
    s.close()

portNumber = <insert server port number here>
args = [[0,0,0],[2,2,2],[1,1,1]] # Points to print in pptk viewer display
positions = numpy.asarray(args, dtype=numpy.float32)
__load(positions)

In the init method of pptk/viewer/viewer.py, you can make the following changes:

Add the line self._portNumber = kwargs.get('port') to grab a port number from the user.

Then, if the user doesn't send in a port number, default to normal behavior like so:

if self._portNumber is None:
    # start up viewer in separate process
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('localhost', 0))
    s.listen(0)
    self._process = subprocess.Popen(
        [os.path.join(_viewer_dir, 'viewer'), str(s.getsockname()[1])],
        stdout=subprocess.PIPE,
        stderr=(None if debug else subprocess.PIPE))
    if debug:
        print ('Started viewer process: %s' \
            % os.path.join(_viewer_dir, 'viewer'))
    x = s.accept()
    self._portNumber = struct.unpack('H', self._process.stdout.read(2))[0]
    # self._portNumber = struct.unpack('H',x[0].recv(2))[0]

After you make these changes, you can use the following line to connect to the existing viewer and use it like normal:

v = pptk.viewer(points, port=1729)
v.attributes(points[:, 2])

@bradylowe

Exception has occurred: ConnectionRefusedError
[Errno 111] Connection refused
  File "mypptk.py", line 24, in <module>
    v = pptk.viewer(xyz, port=36521)