How to use a custom format — GSTPy documentation

# How to use a custom format

## Format specification

For this example we will use the following (made up) example file format.

    TIN
    NAME <name>
    COLOR <r> <g> <b> <a>
    NVERTEX <number_of_vertices>
    <x_coord> <y_coord> <z_coord>
    ...
    <x_coord> <y_coord> <z_coord>
    NTRIANGLE <number_of_triangles>
    <v1_idx> <v2_idx> <v1_idx>
    ...
    <v1_idx> <v2_idx> <v1_idx>
    END

Somre more rules:

- All fields are required and have to appear in the order as specified

- Fields are in all capital letters

- x,y,z coords are floating point values (IEEE 754 double-precision)

- The first point has the index 0, the last point has the index (num_points - 1)

- Triangles are defined by its 3 vertex indices

- v1,v2,v3 idx are integers up to 32 bit (min value: 0, max value: 4294967295)

- r,g,b,a are integers values between 0 and 255

- Files can contain exactly one TIN

Example:

    TIN
    NAME topographie
    COLOR 255 0 0 0
    NVERTEX 4
    0 0 0
    1 0 0
    0 0.5 0
    1 1 0
    NTRIANGLE 2
    0 1 2
    2 1 3
    END

## Code

Imports and some helper functions

First we start with the imports and some small helper functions.

We also define some simple helper types for Point, Triangle and Color.

    """ Example implementation of how to use GSTPy with a custom geometry format"""
    from math import floor
    from collections import namedtuple
    import GSTPy

    def get_gst_connection():
        user = "gstpy_user"
        password = "gstpy_user_pw"
        host = "localhost"
        port = 5432
        db = "gstpy"
        return GSTPy.get_pg_connection(user, password, host, port, db)

    def float_to_int_color_value(float_val):
        """
        return the integer color value for a float color value

        Parameters:
            float_val(float): float color (value between 0 and 1)

        Returns:
            int: the integer color value (value between 0 and 255)
        """
        return int(floor(float_val * 255))

    def int_to_float_color_value(int_val):
        """
        return the float color value for a int color value

        Parameters:
            int_val(int): int color (value between 0 and 255)

        Returns:
            int: the float color value (value between 0 and 1)
        """
        return float(int_val) / 255.

    # we use some simple named tuples for Color, Point and Triangle

    Color = namedtuple('Color', ['r', 'g', 'b', 'a'])

    Point = namedtuple('Point', ['x', 'y', 'z'])

    Triangle = namedtuple('Triangle', ['v1', 'v2', 'v3'])

TIN representation

The TIN representation is rather simple. It has a name, a color, a list of vertices and a list of triangles.

It also has a method to get a `GSTPy.GeometryBuilder` instance from the tin data.

Finally it has a method to read/write a TIN from/to a file.

    class Tin(object):
        """ TIN representation """

        def __init__(self):
            self.name = "default"
            self.color = Color(0, 0, 0, 0)
            self.vertices = []
            self.triangles = []

        def as_geometry_builder(self):
            """ encodes the TIN representation in a GSTPy.GeometryBuilder"""
            gb = GSTPy.GeometryBuilder()
            gb.create_triangle_net3()
            gb.set_name(self.name)
            gb.set_color(
                GSTPy.Color(
                    int_to_float_color_value(self.color.r),
                    int_to_float_color_value(self.color.g),
                    int_to_float_color_value(self.color.b)))
            gb.set_transparency(int_to_float_color_value(255 - self.color.a))
            for point in self.vertices:
                gb.add_point(GSTPy.RawPoint3(point.x, point.y, point.z))

            for triangle in self.triangles:
                gb.add_triangle(
                    GSTPy.IdxTriangle(triangle.v1, triangle.v2, triangle.v3))
            return gb

        def from_file(self, tin_file):
            """ reads a TIN from tin_file """

            def get_line_from_tin_file():
                """
                Returns a line from the tin file
                """
                line = tin_file.readline()
                if line == "":
                    raise RuntimeError("Unexpected end of file")
                line = line.rstrip('\n')
                return line

            def get_line_tokens(first_token):
                """
                Tests if first token is the expected first token and then
                returns the remaining tokens of the line
                """
                line = get_line_from_tin_file()
                tokens = line.split(" ")
                if tokens[0] != first_token:
                    raise RuntimeError("Expected {} line, but got: {}".format(
                        first_token, line))
                return tokens[1:]

            line = get_line_from_tin_file()
            if line != "TIN":
                raise RuntimeError("Expected TIN line, but got: {}".format(line))
            tokens = get_line_tokens("NAME")
            self.name = tokens[0]
            tokens = get_line_tokens("COLOR")
            self.color = Color(
                int(tokens[0]), int(tokens[1]), int(tokens[2]), int(tokens[3]))
            tokens = get_line_tokens("NVERTEX")
            num_vertices = int(tokens[0])
            for i in range(0, num_vertices):
                line = get_line_from_tin_file()
                tokens = line.split(" ")
                self.vertices.append(
                    Point(float(tokens[0]), float(tokens[1]), float(tokens[2])))
            tokens = get_line_tokens("NTRIANGLE")
            num_triangles = int(tokens[0])
            for i in range(0, num_triangles):
                line = get_line_from_tin_file()
                tokens = line.split(" ")
                self.triangles.append(
                    Triangle(int(tokens[0]), int(tokens[1]), int(tokens[2])))
            line = get_line_from_tin_file()
            if line != "END":
                raise RuntimeError("Expected END line, but got: {}".format(line))

        def to_file(self, tin_file):
            """ writes the TIN to tin_file """
            tin_file.write("TIN\n")
            tin_file.write("NAME {}\n".format(self.name))
            tin_file.write("COLOR {} {} {} {}\n".format(
                self.color.r, self.color.g, self.color.b, self.color.a))
            tin_file.write("NVERTEX {}\n".format(len(self.vertices)))
            for vertex in self.vertices:
                tin_file.write("{} {} {}\n".format(vertex.x, vertex.y, vertex.z))
            tin_file.write("NTRIANGLE {}\n".format(len(self.triangles)))
            for triangle in self.triangles:
                tin_file.write("{} {} {}\n".format(triangle.v1, triangle.v2,
                                                   triangle.v3))
            tin_file.write("END\n")

TIN IParsingActions

TinReader is used to retrieve a TIN representation from `GSTPy.NetworkInterface.get_feature_custom_format()`.

    class TinReader(GSTPy.IParsingActions):
        """ Get TIN representation from GSTPy get_feature_custom format"""

        def __init__(self):
            super(TinReader, self).__init__()

            self.tin = Tin()

        def set_name(self, name):
            """
            sets the name for the tin

            Parameters:
                name(str): the name of the GST feature
            """
            self.tin.name = name

        def set_color(self, color):
            """
            sets the color for the tin

            Note:
                GSTPy.color has only r,g,b, the values are floats between 0..1

            Parameters:
                color(GSTPy.color): the color of the GST feature
            """
            self.tin.color = self.tin.color._replace(
                r=float_to_int_color_value(color.r),
                g=float_to_int_color_value(color.g),
                b=float_to_int_color_value(color.b))

        def set_transparency(self, transparency):
            """
            sets the transparency for the tin

            Note:
                value between 0..1

            Parameters:
                transparency(float): the transparency of the GST feature
            """
            self.tin.color = self.tin.color._replace(
                a=float_to_int_color_value(1 - transparency))

        def add_point(self, point):
            """
            adds a point to the tin

            Note:
                GSTPy.RawPoint3 has x,y,z as floats.
                The point indices are in the same order as calls to add_point,
                starting with index=0.
                The v1,v2,v3 values in GSTPy.IdxTriangle reference those indices.

            Parameters:
                point(GSTPy.RawPoint3): a point of the GST feature
            """
            self.tin.vertices.append(Point(point.x, point.y, point.z))

        def add_triangle(self, triangle):
            """
            adds a triangle to the tin

            Note:
                GSTPy.IdxTriangle has v1,v3,v3 as integers.
                The v1,v2,v3 values in GSTPy.IdxTriangle reference the indices
                from add_point.

                For example, the triangle (2, 1, 3) references the points from the
                third, second and fourth add_point call (in that order).

            Parameters:
                triangle(GSTPy.IdxTriangle): a triangle of the GST feature
            """
            self.tin.triangles.append(
                Triangle(triangle.v1, triangle.v2, triangle.v3))

Usage in action

Here we see it all used in action.

See below for the the output of the .tin files.

    ni = get_gst_connection()
    tin = Tin()
    # read the TTIN from a file
    with open(r"test_tin_file.tin", "r") as tin_file:
        tin.from_file(tin_file)
    # get a GeometryBuilder for our read TIN and upload it to GST
    tin_gb = tin.as_geometry_builder()
    fc = ni.create_feature_class("TIN", GSTPy.GeometryTypes.Tin3)
    f = ni.upload_feature(tin_gb, fc)
    # use TinReader to download the TIN format from GST
    tin_cb = TinReader()
    ni.get_feature_custom_format(f, tin_cb)
    tin = tin_cb.tin
    # write the donwloaded TIN to a file
    with open(r"test_tin_file_download.tin", "w") as tin_file:
        tin_cb.tin.to_file(tin_file)
    # append a new 5th vertex (idx=4)
    tin.vertices.append(Point(0, 0, 1))
    # append a new triangle
    tin.triangles.append(Triangle(0, 1, 4))
    # get an updated GeometryBuilder
    tin_gb = tin.as_geometry_builder()
    # update the now changed tin
    ni.instant_update_feature(tin_gb, f)
    # update the now changed tin
    tin_cb = TinReader()
    # download the updated tin
    ni.get_feature_custom_format(f, tin_cb)
    tin = tin_cb.tin
    with open(r"test_tin_file_update.tin", "w") as tin_file:
        tin_cb.tin.to_file(tin_file)
    ni.delete_feature(f)
    ni.delete_feature_class(fc)

## Full code sample

Python source code

    """ Example implementation of how to use GSTPy with a custom geometry format"""
    from math import floor
    from collections import namedtuple
    import GSTPy

    def get_gst_connection():
        user = "gstpy_user"
        password = "gstpy_user_pw"
        host = "localhost"
        port = 5432
        db = "gstpy"
        return GSTPy.get_pg_connection(user, password, host, port, db)

    def float_to_int_color_value(float_val):
        """
        return the integer color value for a float color value

        Parameters:
            float_val(float): float color (value between 0 and 1)

        Returns:
            int: the integer color value (value between 0 and 255)
        """
        return int(floor(float_val * 255))

    def int_to_float_color_value(int_val):
        """
        return the float color value for a int color value

        Parameters:
            int_val(int): int color (value between 0 and 255)

        Returns:
            int: the float color value (value between 0 and 1)
        """
        return float(int_val) / 255.

    # we use some simple named tuples for Color, Point and Triangle

    Color = namedtuple('Color', ['r', 'g', 'b', 'a'])

    Point = namedtuple('Point', ['x', 'y', 'z'])

    Triangle = namedtuple('Triangle', ['v1', 'v2', 'v3'])

    class TinReader(GSTPy.IParsingActions):
        """ Get TIN representation from GSTPy get_feature_custom format"""

        def __init__(self):
            super(TinReader, self).__init__()

            self.tin = Tin()

        def set_name(self, name):
            """
            sets the name for the tin

            Parameters:
                name(str): the name of the GST feature
            """
            self.tin.name = name

        def set_color(self, color):
            """
            sets the color for the tin

            Note:
                GSTPy.color has only r,g,b, the values are floats between 0..1

            Parameters:
                color(GSTPy.color): the color of the GST feature
            """
            self.tin.color = self.tin.color._replace(
                r=float_to_int_color_value(color.r),
                g=float_to_int_color_value(color.g),
                b=float_to_int_color_value(color.b))

        def set_transparency(self, transparency):
            """
            sets the transparency for the tin

            Note:
                value between 0..1

            Parameters:
                transparency(float): the transparency of the GST feature
            """
            self.tin.color = self.tin.color._replace(
                a=float_to_int_color_value(1 - transparency))

        def add_point(self, point):
            """
            adds a point to the tin

            Note:
                GSTPy.RawPoint3 has x,y,z as floats.
                The point indices are in the same order as calls to add_point,
                starting with index=0.
                The v1,v2,v3 values in GSTPy.IdxTriangle reference those indices.

            Parameters:
                point(GSTPy.RawPoint3): a point of the GST feature
            """
            self.tin.vertices.append(Point(point.x, point.y, point.z))

        def add_triangle(self, triangle):
            """
            adds a triangle to the tin

            Note:
                GSTPy.IdxTriangle has v1,v3,v3 as integers.
                The v1,v2,v3 values in GSTPy.IdxTriangle reference the indices
                from add_point.

                For example, the triangle (2, 1, 3) references the points from the
                third, second and fourth add_point call (in that order).

            Parameters:
                triangle(GSTPy.IdxTriangle): a triangle of the GST feature
            """
            self.tin.triangles.append(
                Triangle(triangle.v1, triangle.v2, triangle.v3))

    class Tin(object):
        """ TIN representation """

        def __init__(self):
            self.name = "default"
            self.color = Color(0, 0, 0, 0)
            self.vertices = []
            self.triangles = []

        def as_geometry_builder(self):
            """ encodes the TIN representation in a GSTPy.GeometryBuilder"""
            gb = GSTPy.GeometryBuilder()
            gb.create_triangle_net3()
            gb.set_name(self.name)
            gb.set_color(
                GSTPy.Color(
                    int_to_float_color_value(self.color.r),
                    int_to_float_color_value(self.color.g),
                    int_to_float_color_value(self.color.b)))
            gb.set_transparency(int_to_float_color_value(255 - self.color.a))
            for point in self.vertices:
                gb.add_point(GSTPy.RawPoint3(point.x, point.y, point.z))

            for triangle in self.triangles:
                gb.add_triangle(
                    GSTPy.IdxTriangle(triangle.v1, triangle.v2, triangle.v3))
            return gb

        def from_file(self, tin_file):
            """ reads a TIN from tin_file """

            def get_line_from_tin_file():
                """
                Returns a line from the tin file
                """
                line = tin_file.readline()
                if line == "":
                    raise RuntimeError("Unexpected end of file")
                line = line.rstrip('\n')
                return line

            def get_line_tokens(first_token):
                """
                Tests if first token is the expected first token and then
                returns the remaining tokens of the line
                """
                line = get_line_from_tin_file()
                tokens = line.split(" ")
                if tokens[0] != first_token:
                    raise RuntimeError("Expected {} line, but got: {}".format(
                        first_token, line))
                return tokens[1:]

            line = get_line_from_tin_file()
            if line != "TIN":
                raise RuntimeError("Expected TIN line, but got: {}".format(line))
            tokens = get_line_tokens("NAME")
            self.name = tokens[0]
            tokens = get_line_tokens("COLOR")
            self.color = Color(
                int(tokens[0]), int(tokens[1]), int(tokens[2]), int(tokens[3]))
            tokens = get_line_tokens("NVERTEX")
            num_vertices = int(tokens[0])
            for i in range(0, num_vertices):
                line = get_line_from_tin_file()
                tokens = line.split(" ")
                self.vertices.append(
                    Point(float(tokens[0]), float(tokens[1]), float(tokens[2])))
            tokens = get_line_tokens("NTRIANGLE")
            num_triangles = int(tokens[0])
            for i in range(0, num_triangles):
                line = get_line_from_tin_file()
                tokens = line.split(" ")
                self.triangles.append(
                    Triangle(int(tokens[0]), int(tokens[1]), int(tokens[2])))
            line = get_line_from_tin_file()
            if line != "END":
                raise RuntimeError("Expected END line, but got: {}".format(line))

        def to_file(self, tin_file):
            """ writes the TIN to tin_file """
            tin_file.write("TIN\n")
            tin_file.write("NAME {}\n".format(self.name))
            tin_file.write("COLOR {} {} {} {}\n".format(
                self.color.r, self.color.g, self.color.b, self.color.a))
            tin_file.write("NVERTEX {}\n".format(len(self.vertices)))
            for vertex in self.vertices:
                tin_file.write("{} {} {}\n".format(vertex.x, vertex.y, vertex.z))
            tin_file.write("NTRIANGLE {}\n".format(len(self.triangles)))
            for triangle in self.triangles:
                tin_file.write("{} {} {}\n".format(triangle.v1, triangle.v2,
                                                   triangle.v3))
            tin_file.write("END\n")

    def main():
        ni = get_gst_connection()
        tin = Tin()
        # read the TTIN from a file
        with open(r"test_tin_file.tin", "r") as tin_file:
            tin.from_file(tin_file)
        # get a GeometryBuilder for our read TIN and upload it to GST
        tin_gb = tin.as_geometry_builder()
        fc = ni.create_feature_class("TIN", GSTPy.GeometryTypes.Tin3)
        f = ni.upload_feature(tin_gb, fc)
        # use TinReader to download the TIN format from GST
        tin_cb = TinReader()
        ni.get_feature_custom_format(f, tin_cb)
        tin = tin_cb.tin
        # write the donwloaded TIN to a file
        with open(r"test_tin_file_download.tin", "w") as tin_file:
            tin_cb.tin.to_file(tin_file)
        # append a new 5th vertex (idx=4)
        tin.vertices.append(Point(0, 0, 1))
        # append a new triangle
        tin.triangles.append(Triangle(0, 1, 4))
        # get an updated GeometryBuilder
        tin_gb = tin.as_geometry_builder()
        # update the now changed tin
        ni.instant_update_feature(tin_gb, f)
        # update the now changed tin
        tin_cb = TinReader()
        # download the updated tin
        ni.get_feature_custom_format(f, tin_cb)
        tin = tin_cb.tin
        with open(r"test_tin_file_update.tin", "w") as tin_file:
            tin_cb.tin.to_file(tin_file)
        ni.delete_feature(f)
        ni.delete_feature_class(fc)

    if __name__ == "__main__":
        main()

.tin files

test_tin_file.tin

    TIN
    NAME topographie
    COLOR 255 0 0 0
    NVERTEX 4
    0 0 0
    1 0 0
    0 0.5 0
    1 1 0
    NTRIANGLE 2
    0 1 2
    2 1 3
    END

test_tin_file_download.tin

    TIN
    NAME topographie
    COLOR 255 0 0 0
    NVERTEX 4
    0.0 0.0 0.0
    1.0 0.0 0.0
    0.0 0.5 0.0
    1.0 1.0 0.0
    NTRIANGLE 2
    0 1 2
    2 1 3
    END

test_tin_file_update.tin

    TIN
    NAME topographie
    COLOR 255 0 0 0
    NVERTEX 5
    0.0 0.0 0.0
    1.0 0.0 0.0
    0.0 0.5 0.0
    1.0 1.0 0.0
    0.0 0.0 1.0
    NTRIANGLE 3
    0 1 2
    2 1 3
    0 1 4
    END