diff --git a/backend/gRPCProjector/Dockerfile b/backend/gRPCProjector/node/Dockerfile
similarity index 100%
rename from backend/gRPCProjector/Dockerfile
rename to backend/gRPCProjector/node/Dockerfile
diff --git a/backend/gRPCProjector/package-lock.json b/backend/gRPCProjector/node/package-lock.json
similarity index 100%
rename from backend/gRPCProjector/package-lock.json
rename to backend/gRPCProjector/node/package-lock.json
diff --git a/backend/gRPCProjector/package.json b/backend/gRPCProjector/node/package.json
similarity index 100%
rename from backend/gRPCProjector/package.json
rename to backend/gRPCProjector/node/package.json
diff --git a/backend/gRPCProjector/protos/README.md b/backend/gRPCProjector/node/protos/README.md
similarity index 100%
rename from backend/gRPCProjector/protos/README.md
rename to backend/gRPCProjector/node/protos/README.md
diff --git a/backend/gRPCProjector/protos/v3/projector.proto b/backend/gRPCProjector/node/protos/v3/projector.proto
similarity index 100%
rename from backend/gRPCProjector/protos/v3/projector.proto
rename to backend/gRPCProjector/node/protos/v3/projector.proto
diff --git a/backend/gRPCProjector/src/grpcServer.js b/backend/gRPCProjector/node/src/grpcServer.js
similarity index 100%
rename from backend/gRPCProjector/src/grpcServer.js
rename to backend/gRPCProjector/node/src/grpcServer.js
diff --git a/backend/gRPCProjector/python/__pycache__/projector_pb2.cpython-38.pyc b/backend/gRPCProjector/python/__pycache__/projector_pb2.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0812ba7ac0dafc317f002a7f310df4c4bd25e7e5
Binary files /dev/null and b/backend/gRPCProjector/python/__pycache__/projector_pb2.cpython-38.pyc differ
diff --git a/backend/gRPCProjector/python/__pycache__/projector_pb2_grpc.cpython-38.pyc b/backend/gRPCProjector/python/__pycache__/projector_pb2_grpc.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f95009adeb8c6035fbca843d5f5745e6c5051400
Binary files /dev/null and b/backend/gRPCProjector/python/__pycache__/projector_pb2_grpc.cpython-38.pyc differ
diff --git a/backend/gRPCProjector/python/grpcServer.py b/backend/gRPCProjector/python/grpcServer.py
new file mode 100644
index 0000000000000000000000000000000000000000..78db1b041024e0ce07c19779220427c266290efc
--- /dev/null
+++ b/backend/gRPCProjector/python/grpcServer.py
@@ -0,0 +1,42 @@
+from concurrent import futures
+import grpc
+import projector_pb2_grpc as rpc
+
+import projector_pb2 as projector
+
+
+
+class ProjectorService(rpc.ProjectorServicer):
+
+    def __init__(self):
+        self.pointID = 0
+    
+    def getProjectionPoints(self, request_iterator, context):
+        print("Connected")
+        for message in request_iterator:
+            yield self.rowToPoint(message)
+
+    def rowToPoint(self, row):
+        hdvector = row.hdvector
+        returnPoint = projector.Point(id=self.pointID, x = float(hdvector[0]), y=float(hdvector[1]))
+        self.pointID += 1
+
+
+        if self.pointID % 1000 == 0:
+            print("Received {} points!".format(self.pointID))
+        return returnPoint
+    
+def serveServer():
+    port = '[::]:50051'
+    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
+    rpc.add_ProjectorServicer_to_server(ProjectorService(), server)
+    server.add_insecure_port(port)
+    server.start()
+    print("Listening on port: " + port)
+    server.wait_for_termination()
+
+def serveLocal():
+    return
+    
+if __name__ == '__main__':
+    serveServer()
diff --git a/backend/gRPCProjector/python/projector_pb2.py b/backend/gRPCProjector/python/projector_pb2.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f34f7013e3463ae66d6eccbf6a8ba4069907ad1
--- /dev/null
+++ b/backend/gRPCProjector/python/projector_pb2.py
@@ -0,0 +1,239 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: projector.proto
+
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='projector.proto',
+  package='provee',
+  syntax='proto3',
+  serialized_options=b'\n\017nl.uuvig.proveeB\022ProveProjectorGRCPP\001\242\002\006PROVEE',
+  create_key=_descriptor._internal_create_key,
+  serialized_pb=b'\n\x0fprojector.proto\x12\x06provee\x1a\x1bgoogle/protobuf/empty.proto\")\n\x05Point\x12\n\n\x02id\x18\x01 \x01(\x05\x12\t\n\x01x\x18\x02 \x01(\x02\x12\t\n\x01y\x18\x03 \x01(\x02\"D\n\x0bTrainingSet\x12\x0f\n\x07modelid\x18\x01 \x01(\t\x12$\n\x04rows\x18\x02 \x03(\x0b\x32\x16.provee.TrainingSetRow\"2\n\x0eTrainingSetRow\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x08hdvector\x18\x02 \x03(\x01\x42\x02\x10\x01\x32\xf9\x01\n\tProjector\x12\x37\n\x05start\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12\x36\n\x04stop\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12\x42\n\x13getProjectionPoints\x12\x16.provee.TrainingSetRow\x1a\r.provee.Point\"\x00(\x01\x30\x01\x12\x37\n\ngetUpdates\x12\x16.google.protobuf.Empty\x1a\r.provee.Point\"\x00\x30\x01\x42\x30\n\x0fnl.uuvig.proveeB\x12ProveProjectorGRCPP\x01\xa2\x02\x06PROVEEb\x06proto3'
+  ,
+  dependencies=[google_dot_protobuf_dot_empty__pb2.DESCRIPTOR,])
+
+
+
+
+_POINT = _descriptor.Descriptor(
+  name='Point',
+  full_name='provee.Point',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='provee.Point.id', index=0,
+      number=1, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='x', full_name='provee.Point.x', index=1,
+      number=2, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='y', full_name='provee.Point.y', index=2,
+      number=3, type=2, cpp_type=6, label=1,
+      has_default_value=False, default_value=float(0),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=56,
+  serialized_end=97,
+)
+
+
+_TRAININGSET = _descriptor.Descriptor(
+  name='TrainingSet',
+  full_name='provee.TrainingSet',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='modelid', full_name='provee.TrainingSet.modelid', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='rows', full_name='provee.TrainingSet.rows', index=1,
+      number=2, type=11, cpp_type=10, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=99,
+  serialized_end=167,
+)
+
+
+_TRAININGSETROW = _descriptor.Descriptor(
+  name='TrainingSetRow',
+  full_name='provee.TrainingSetRow',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  create_key=_descriptor._internal_create_key,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='id', full_name='provee.TrainingSetRow.id', index=0,
+      number=1, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=b"".decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    _descriptor.FieldDescriptor(
+      name='hdvector', full_name='provee.TrainingSetRow.hdvector', index=1,
+      number=2, type=1, cpp_type=5, label=3,
+      has_default_value=False, default_value=[],
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=b'\020\001', file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto3',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=169,
+  serialized_end=219,
+)
+
+_TRAININGSET.fields_by_name['rows'].message_type = _TRAININGSETROW
+DESCRIPTOR.message_types_by_name['Point'] = _POINT
+DESCRIPTOR.message_types_by_name['TrainingSet'] = _TRAININGSET
+DESCRIPTOR.message_types_by_name['TrainingSetRow'] = _TRAININGSETROW
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+Point = _reflection.GeneratedProtocolMessageType('Point', (_message.Message,), {
+  'DESCRIPTOR' : _POINT,
+  '__module__' : 'projector_pb2'
+  # @@protoc_insertion_point(class_scope:provee.Point)
+  })
+_sym_db.RegisterMessage(Point)
+
+TrainingSet = _reflection.GeneratedProtocolMessageType('TrainingSet', (_message.Message,), {
+  'DESCRIPTOR' : _TRAININGSET,
+  '__module__' : 'projector_pb2'
+  # @@protoc_insertion_point(class_scope:provee.TrainingSet)
+  })
+_sym_db.RegisterMessage(TrainingSet)
+
+TrainingSetRow = _reflection.GeneratedProtocolMessageType('TrainingSetRow', (_message.Message,), {
+  'DESCRIPTOR' : _TRAININGSETROW,
+  '__module__' : 'projector_pb2'
+  # @@protoc_insertion_point(class_scope:provee.TrainingSetRow)
+  })
+_sym_db.RegisterMessage(TrainingSetRow)
+
+
+DESCRIPTOR._options = None
+_TRAININGSETROW.fields_by_name['hdvector']._options = None
+
+_PROJECTOR = _descriptor.ServiceDescriptor(
+  name='Projector',
+  full_name='provee.Projector',
+  file=DESCRIPTOR,
+  index=0,
+  serialized_options=None,
+  create_key=_descriptor._internal_create_key,
+  serialized_start=222,
+  serialized_end=471,
+  methods=[
+  _descriptor.MethodDescriptor(
+    name='start',
+    full_name='provee.Projector.start',
+    index=0,
+    containing_service=None,
+    input_type=google_dot_protobuf_dot_empty__pb2._EMPTY,
+    output_type=google_dot_protobuf_dot_empty__pb2._EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='stop',
+    full_name='provee.Projector.stop',
+    index=1,
+    containing_service=None,
+    input_type=google_dot_protobuf_dot_empty__pb2._EMPTY,
+    output_type=google_dot_protobuf_dot_empty__pb2._EMPTY,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='getProjectionPoints',
+    full_name='provee.Projector.getProjectionPoints',
+    index=2,
+    containing_service=None,
+    input_type=_TRAININGSETROW,
+    output_type=_POINT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+  _descriptor.MethodDescriptor(
+    name='getUpdates',
+    full_name='provee.Projector.getUpdates',
+    index=3,
+    containing_service=None,
+    input_type=google_dot_protobuf_dot_empty__pb2._EMPTY,
+    output_type=_POINT,
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+  ),
+])
+_sym_db.RegisterServiceDescriptor(_PROJECTOR)
+
+DESCRIPTOR.services_by_name['Projector'] = _PROJECTOR
+
+# @@protoc_insertion_point(module_scope)
diff --git a/backend/gRPCProjector/python/projector_pb2_grpc.py b/backend/gRPCProjector/python/projector_pb2_grpc.py
new file mode 100644
index 0000000000000000000000000000000000000000..f36b3e05eee43d386a43cd7478846f73688d6141
--- /dev/null
+++ b/backend/gRPCProjector/python/projector_pb2_grpc.py
@@ -0,0 +1,183 @@
+# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
+"""Client and server classes corresponding to protobuf-defined services."""
+import grpc
+
+from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
+import projector_pb2 as projector__pb2
+
+
+class ProjectorStub(object):
+    """Interface exported by the server.
+    """
+
+    def __init__(self, channel):
+        """Constructor.
+
+        Args:
+            channel: A grpc.Channel.
+        """
+        self.start = channel.unary_unary(
+                '/provee.Projector/start',
+                request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+                response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+                )
+        self.stop = channel.unary_unary(
+                '/provee.Projector/stop',
+                request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+                response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+                )
+        self.getProjectionPoints = channel.stream_stream(
+                '/provee.Projector/getProjectionPoints',
+                request_serializer=projector__pb2.TrainingSetRow.SerializeToString,
+                response_deserializer=projector__pb2.Point.FromString,
+                )
+        self.getUpdates = channel.unary_stream(
+                '/provee.Projector/getUpdates',
+                request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+                response_deserializer=projector__pb2.Point.FromString,
+                )
+
+
+class ProjectorServicer(object):
+    """Interface exported by the server.
+    """
+
+    def start(self, request, context):
+        """A simple RPC.
+
+        Start the Projector calculation
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def stop(self, request, context):
+        """Stop the Projector calculation
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def getProjectionPoints(self, request_iterator, context):
+        """A server-to-client streaming RPC.
+
+        Obtains the Projection Points in 2D given the trainings values stored in a row format.  Results are
+        streamed rather than returned at once (e.g. in a response message with a
+        repeated field).
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+    def getUpdates(self, request, context):
+        """A server-to-client streaming RPC.
+
+        Obtains the Projection Points in 2D given the trainings values stored in a row format.  Results are
+        streamed rather than returned at once (e.g. in a response message with a
+        repeated field).
+        """
+        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+        context.set_details('Method not implemented!')
+        raise NotImplementedError('Method not implemented!')
+
+
+def add_ProjectorServicer_to_server(servicer, server):
+    rpc_method_handlers = {
+            'start': grpc.unary_unary_rpc_method_handler(
+                    servicer.start,
+                    request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+                    response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+            ),
+            'stop': grpc.unary_unary_rpc_method_handler(
+                    servicer.stop,
+                    request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+                    response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+            ),
+            'getProjectionPoints': grpc.stream_stream_rpc_method_handler(
+                    servicer.getProjectionPoints,
+                    request_deserializer=projector__pb2.TrainingSetRow.FromString,
+                    response_serializer=projector__pb2.Point.SerializeToString,
+            ),
+            'getUpdates': grpc.unary_stream_rpc_method_handler(
+                    servicer.getUpdates,
+                    request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+                    response_serializer=projector__pb2.Point.SerializeToString,
+            ),
+    }
+    generic_handler = grpc.method_handlers_generic_handler(
+            'provee.Projector', rpc_method_handlers)
+    server.add_generic_rpc_handlers((generic_handler,))
+
+
+ # This class is part of an EXPERIMENTAL API.
+class Projector(object):
+    """Interface exported by the server.
+    """
+
+    @staticmethod
+    def start(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(request, target, '/provee.Projector/start',
+            google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+            google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def stop(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_unary(request, target, '/provee.Projector/stop',
+            google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+            google_dot_protobuf_dot_empty__pb2.Empty.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def getProjectionPoints(request_iterator,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.stream_stream(request_iterator, target, '/provee.Projector/getProjectionPoints',
+            projector__pb2.TrainingSetRow.SerializeToString,
+            projector__pb2.Point.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
+
+    @staticmethod
+    def getUpdates(request,
+            target,
+            options=(),
+            channel_credentials=None,
+            call_credentials=None,
+            insecure=False,
+            compression=None,
+            wait_for_ready=None,
+            timeout=None,
+            metadata=None):
+        return grpc.experimental.unary_stream(request, target, '/provee.Projector/getUpdates',
+            google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
+            projector__pb2.Point.FromString,
+            options, channel_credentials,
+            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
diff --git a/backend/gRPCProjector/python/protos/README.md b/backend/gRPCProjector/python/protos/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..128e4b2fa1fce1c43af79e2956e931e79b266d90
--- /dev/null
+++ b/backend/gRPCProjector/python/protos/README.md
@@ -0,0 +1,3 @@
+# protos
+
+Contains all Protos (gRCP) files that are used to communicate between services in Provee
\ No newline at end of file
diff --git a/backend/gRPCProjector/python/protos/v3/projector.proto b/backend/gRPCProjector/python/protos/v3/projector.proto
new file mode 100644
index 0000000000000000000000000000000000000000..de425c04e965f933bfdeac12364f6048b8374035
--- /dev/null
+++ b/backend/gRPCProjector/python/protos/v3/projector.proto
@@ -0,0 +1,71 @@
+syntax = "proto3";
+
+package provee;
+
+import "google/protobuf/empty.proto";
+
+option java_multiple_files = true;
+option java_package = "nl.uuvig.provee";
+option java_outer_classname = "ProveProjectorGRCP";
+option objc_class_prefix = "PROVEE";
+
+
+// Interface exported by the server.
+service Projector {
+  // A simple RPC.
+  //
+  // Start the Projector calculation
+  rpc start(google.protobuf.Empty) returns (google.protobuf.Empty);
+
+  // Stop the Projector calculation
+  rpc stop(google.protobuf.Empty) returns (google.protobuf.Empty);
+
+  // A server-to-client streaming RPC.
+  //
+  // Obtains the Projection Points in 2D given the trainings values stored in a row format.  Results are
+  // streamed rather than returned at once (e.g. in a response message with a
+  // repeated field).
+  rpc getProjectionPoints(stream TrainingSetRow) returns (stream Point) {}
+
+  // A server-to-client streaming RPC.
+  //
+  // Obtains the Projection Points in 2D given the trainings values stored in a row format.  Results are
+  // streamed rather than returned at once (e.g. in a response message with a
+  // repeated field).
+  rpc getUpdates(google.protobuf.Empty) returns (stream Point) {}
+
+//   // A client-to-server streaming RPC.
+//   //
+//   // Accepts a stream of Points on a route being traversed, returning a
+//   // RouteSummary when traversal is completed.
+//   rpc RecordRoute(stream Point) returns (RouteSummary) {}
+
+//   // A Bidirectional streaming RPC.
+//   //
+//   // Accepts a stream of RouteNotes sent while a route is being traversed,
+//   // while receiving other RouteNotes (e.g. from other users).
+//   rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
+}
+
+// Points are represented as x-y pairs
+message Point {
+  int32 id = 1;
+  float x = 2;
+  float y = 3;
+}
+
+// Needs description
+message TrainingSet {
+  string modelid = 1;
+  repeated TrainingSetRow rows = 2;
+}
+
+// Needs documentation
+message TrainingSetRow {
+  // The id of the row, e.g., row index.
+  string id = 1;
+
+  // The hd vector of the item.
+  repeated double hdvector = 2 [packed=true];
+}
+