def convert_token(tag, value):
    '''
    Given a LandXML tag and it's value, return it
    as the data type specified in KeyMaps
    '''

    if not tag or not value:
        return None

    _typ = None

    for _k, _v in KeyMaps.XML_TYPES.items():

        if tag in _v:
            _typ = _k
            break

    if not _typ or _typ == 'string':
        return value

    if _typ == 'int':
        return Utils.to_int(value)

    if _typ == 'float':
        return Utils.to_float(value)

    if _typ == 'vector':
        coords = Utils.to_float(value.split(' '))

        if coords:
            return App.Vector(coords)

    return None
Exemple #2
0
    def _parse_data(self, align_name, tags, attrib):
        '''
        Build a dictionary keyed to the internal attribute names from the XML
        '''
        result = {}

        #test to ensure all required tags are in the imrpoted XML data
        missing_tags = set(tags[0]).difference(set(list(attrib.keys())))

        #report error and skip the alignment if required tags are missing
        if missing_tags:
            self.errors.append(
                'The required XML tags were not found in alignment %s:\n%s' %
                (align_name, missing_tags))
            return None

        #merge the required / optional tag dictionaries and iterate the items
        for _tag in tags[0] + tags[1]:

            attr_val = LandXml.convert_token(_tag, attrib.get(_tag))

            if attr_val is None:

                if _tag in tags[0]:
                    self.errors.append(
                        'Missing or invalid %s attribute in alignment %s' %
                        (_tag, align_name))

            #test for angles and convert to radians
            elif _tag in maps.XML_TAGS['angle']:
                attr_val = Utils.to_float(attrib.get(_tag))

                if attr_val:
                    attr_val = math.radians(attr_val)

            #test for lengths to convert to mm
            elif _tag in maps.XML_TAGS['length']:
                attr_val = Utils.to_float(attrib.get(_tag))

                if attr_val:
                    attr_val = attr_val * Units.scale_factor()

            #convert rotation from string to number
            elif _tag == 'rot':

                attr_val = 0.0

                if attrib.get(_tag) == 'cw':
                    attr_val = 1.0

                elif attrib.get(_tag) == 'ccw':
                    attr_val = -1.0

            if not attr_val:
                attr_val = LandXml.get_tag_default(_tag)

            result[maps.XML_MAP[_tag]] = attr_val

        return result
def get_bearings(arc, mat, delta, rot):
    '''
    Calculate the bearings from the matrix and delta value
    '''

    bearing_in = arc.get('BearingIn')
    bearing_out = arc.get('BearingOut')

    bearings = []

    for _i in range(0, 6):
        bearings.append(_GEO.FUNC[6][_i](mat.A[6][_i], delta, rot))

    _b = [_v % C.TWO_PI for _v in bearings[0:6] if Utils.to_float(_v)]

    if _b:

        #check to ensure all tangent start bearing values are identical
        if not Support.within_tolerance(_b):
            return None

        #default to calculated if different from supplied bearing
        if not Support.within_tolerance(_b[0], bearing_in):
            bearing_in = _b[0]

    if not bearing_in:
        return None

    _b_out = bearing_in + (delta * rot)

    if not Support.within_tolerance(_b_out, bearing_out):
        bearing_out = _b_out

    _row = mat.A[6]

    _rad = [_row[0], _row[1]]
    _tan = [bearing_in, bearing_out]
    _int = [_row[4], _row[5]]

    if not Utils.to_float(_rad[0]):
        _rad[0] = bearing_in - rot * (C.HALF_PI)

    if not Utils.to_float(_rad[1]):
        _rad[1] = _rad[0] + rot * delta

    if not Utils.to_float(_int[0]):
        _int[0] = _rad[0] + rot * (delta / 2.0)

    if not Utils.to_float(_int[1]):
        _int[1] = _rad[0] + rot * ((math.pi + delta) / 2.0)

    mat_bearings = {'Radius': _rad, 'Tangent': _tan, 'Internal': _int}

    return {
        'BearingIn': bearing_in,
        'BearingOut': bearing_out,
        'Bearings': mat_bearings
    }
Exemple #4
0
def vector_from_angle(angle):
    '''
    Returns a vector form a given angle in radians
    '''

    _angle = Utils.to_float(angle)

    if not _angle:
        return None

    return App.Vector(math.sin(_angle), math.cos(_angle), 0.0)
def build_vector(coords):
    '''
    Returns an App.Vector of the passed coordinates,
    ensuring they are float-compatible
    '''

    if not coords:
        return None

    float_coords = Utils.to_float(coords)

    if not all(float_coords):
        return None

    return App.Vector(float_coords)
    def assign_station_data(self):
        '''
        Assign the station and intersection equation data
        '''

        obj = self.Object

        _eqs = self.geometry['station']
        _meta = self.geometry['meta']

        _eqn_list = []
        _start_sta = Utils.to_float(_meta.get('StartStation'))

        if _start_sta:
            _eqn_list.append(App.Vector(0.0, _start_sta, 0.0))
        else:
            self.errors.append('Unable to convert starting station %s' %
                               _meta.get('StartStation'))

        for _eqn in _eqs:

            eq_vec = None

            #default to increasing station if unspecified
            if not _eqn['Direction']:
                _eqn['Direction'] = 1.0

            try:
                eq_vec = App.Vector(float(_eqn['Back']), float(_eqn['Ahead']),
                                    float(_eqn['Direction']))

            except:
                self.errors.append('Unable to convert station equation (%s)' %
                                   (_eqn))
                continue

            _eqn_list.append(eq_vec)

        obj.Station_Equations = _eqn_list
def get_delta(arc, mat):
    '''
    Get the delta value from the matrix, if possible,
    Default to the user-provided parameter if no calculated
    or values within tolerance
    '''
    #get the delta from the arc data as a default
    delta = arc.get('Delta')

    if not delta:
        if arc.get('BearingIn') and arc.get('BearingOut'):
            delta = abs(arc['BearingOut'] - arc['BearingIn'])

    #find the first occurence of the delta value
    for _i in range(1, 6):
        for _j in range(0, _i):
            if Utils.to_float(mat.A[_i][_j]):
                delta = mat.A[_i][_j]
                break

    if not delta:
        return None

    return {'Delta': delta}
    def _discretize_geometry(self):
        """
        Discretizes the alignment geometry to a series of vector points
        """

        interval = self.Object.Seg_Value
        interval_type = self.Object.Method

        #alignment construction requires a 'look ahead' at the next element
        #This implementation does a 'look back' at the previous.
        #Thus, iteration starts at the second element and
        #the last element is duplicated to ensure it is constructed.
        geometry = self.Object.Geometry

        if not geometry:
            print('No geometry defined.  Unnable to discretize')
            return None

        prev_geo = [float(_i) for _i in geometry[0].split(',')]

        #test in case we only have one geometric element
        if len(geometry) > 1:
            geometry = geometry[1:]
            geometry.append(geometry[-1])

        prev_curve_tangent = 0.0

        coords = [App.Vector(0.0, 0.0, 0.0)]

        for geo_string in geometry:

            #convert the commo-delimited string of floats into a list of strings, then to floats
            _geo = [float(_i) for _i in geo_string.split(',')]

            bearing_in = math.radians(prev_geo[1])
            bearing_out = math.radians(_geo[1])

            curve_dir, central_angle = Utils.directed_angle(
                Utils.vector_from_angle(bearing_in),
                Utils.vector_from_angle(bearing_out))

            curve_tangent = prev_geo[2] * math.tan(central_angle / 2.0)

            #alternate calculation for spiral curves
            if prev_geo[3] > 0.0:
                curve_tangent = (prev_geo[3] / 2.0) + (prev_geo[2] + (
                    (prev_geo[3]**2) /
                    (24 * prev_geo[2]))) * math.tan(central_angle / 2.0)

            #previous tangent length = distance between PI's minus the two curve tangents
            prev_tan_len = prev_geo[0] - curve_tangent - prev_curve_tangent

            #skip if our tangent length is too short leadng up to a curve (likely a compound curve)
            if prev_tan_len >= 1:
                coords.extend(
                    self.discretize_arc(coords[-1], bearing_in, prev_tan_len,
                                        0.0, 0.0, 'Segment'))

            #zero radius means no curve.  We're done
            if prev_geo[2] > 0.0:
                if prev_geo[3] > 0.0:
                    coords.extend(
                        self.discretize_spiral(coords[-1], bearing_in,
                                               prev_geo[2],
                                               central_angle * curve_dir,
                                               prev_geo[3], interval,
                                               interval_type))
                else:
                    coords.extend(
                        self.discretize_arc(coords[-1], bearing_in,
                                            prev_geo[2],
                                            central_angle * curve_dir,
                                            interval, interval_type))

            prev_geo = _geo
            prev_curve_tangent = curve_tangent

        return coords
    def assign_geometry_data(self, datum, data):
        """
        Iterate the dataset, extracting geometric data
        Validate data
        Create separate items for each curve / tangent
        Assign Northing / Easting datums
        """

        _points = [datum]
        _geometry = []

        for item in data:

            _ne = [item['Northing'], item['Easting']]
            _db = [item['Distance'], item['Bearing']]
            _rd = [item['Radius'], item['Degree']]

            curve = [0.0, 0.0, 0.0, 0.0]

            try:
                curve[3] = float(item['Spiral'])

            except:
                pass

            point_vector = App.Vector(0.0, 0.0, 0.0)

            #parse degree of curve / radius values
            if _rd[0]:
                try:
                    curve[2] = float(_rd[0])
                except:
                    self.errors.append('Invalid radius: %s' % _rd[0])

            elif _rd[1]:
                try:
                    curve[2] = Utils.doc_to_radius(float(_rd[1]))
                except:
                    self.errors.append('Invalid degree of curve: %s' % _rd[1])
            else:
                self.errors.append('Missing Radius / Degree of Curvature')

            #parse bearing / distance values
            if any(_db) and not all(_db):
                self.errors.append('(Distance, Bearing) Incomplete: (%s, %s)' %
                                   tuple(_db))

            elif all(_db):
                #get the last point in the geometry for the datum, unless empty
                datum = self.Object.Datum

                try:
                    _db[0] = float(_db[0])
                    _db[1] = float(_db[1])

                    #zero distance means coincident PI's.  Skip that.
                    if _db[0] > 0.0:

                        #set values to geo_vector, adding the previous position to the new one
                        point_vector = Utils.distance_bearing_to_coordinates(
                            _db[0], _db[1])
                        curve[0:2] = _db

                        if not Units.is_metric_doc():
                            point_vector.multiply(304.8)

                        point_vector = _points[-1].add(point_vector)

                except:
                    self.errors.append(
                        '(Distance, Bearing) Invalid: (%s, %s)' % tuple(_db))

            #parse northing / easting values
            if any(_ne) and not all(_ne):
                self.errors.append(
                    '(Easting, Northing) Incomplete: ( %s, %s)' % tuple(_ne))

            elif all(_ne):

                try:
                    point_vector.y = float(_ne[0])
                    point_vector.x = float(_ne[1])

                except:
                    self.errors.append(
                        '(Easting, Northing) Invalid: (%s, %s)' % tuple(_ne))

                curve[0:2] = Utils.coordinates_to_distance_bearing(
                    _points[-1], point_vector)

            #skip coincident PI's
            #save the point as a vector
            #save the geometry as a string
            if curve[0] > 0.0:
                _points.append(point_vector)
                print('saving ', str(curve))
                _geometry.append(str(curve).replace('[', '').replace(']', ''))

        self.Object.PIs = _points
        self.Object.Geometry = _geometry
    def _discretize_geometry(self, segments):
        '''
        Discretizes the alignment geometry to a series of vector points
        '''

        print('discretizing ', self.Object.Geometry)

        #alignment construction requires a 'look ahead' at the next element
        #This implementation does a 'look back' at the previous.
        #Thus, iteration starts at the second element and
        #the last element is duplicated to ensure it is constructed.
        geometry = self.Object.Geometry

        if not geometry:
            print('No geometry defined.  Unnable to discretize')
            return None

        prev_geo = geometry[0]

        #test in case we only have one geometric element
        if len(geometry) > 1:
            geometry = geometry[1:]
            geometry.append(geometry[-1])

        prev_coord = App.Vector(0.0, 0.0, 0.0)
        prev_curve_tangent = 0.0

        coords = [App.Vector(0.0, 0.0, 0.0)]

        for _geo in geometry:

            print('\n-----=======>>>>>', _geo)

            distance = prev_geo[0]
            bearing_in = math.radians(prev_geo[1])
            bearing_out = math.radians(_geo[1])

            in_tangent = Utils.vector_from_angle(bearing_in)
            out_tangent = Utils.vector_from_angle(bearing_out)

            curve_dir, central_angle = Utils.directed_angle(
                in_tangent, out_tangent)

            radius = prev_geo[2]

            #if both the current geometry and the look-back geometry have nno-zero radii,
            #we're between curves
            between_curves = radius > 0.0 and _geo[2] > 0.0

            curve_tangent = radius * math.tan(central_angle / 2.0)
            prev_tan_len = distance - curve_tangent - prev_curve_tangent
            _forward = App.Vector(math.sin(bearing_in), math.cos(bearing_in),
                                  0.0)

            print('bearing in ', bearing_in)
            print('bearing_out ', bearing_out)
            print('distance ', distance)
            print('in-tangent ', in_tangent)
            print('out-tangent ', out_tangent)
            print('curve-dir ', curve_dir)
            print('central angle ', central_angle)
            print('radius ', radius)
            print('between curves? ', between_curves)
            print('prev_tan_len ', prev_tan_len)
            print('curve tangent ', curve_tangent)

            #skip if our tangent length is too short leadng up to a curve (likely a compound curve)
            if prev_tan_len >= 1:  #DocumentProperties.MinimumTangentLength.get_value() or not between_curves:

                #append three coordinates:
                #   one a millimeter away from the starting point
                #   one a millimeter away from the ending point
                #   one at the end point
                mm_tan_len = prev_tan_len * 304.80

                coords.append(prev_coord.add(_forward))
                coords.append(
                    prev_coord.add(
                        App.Vector(_forward).multiply(mm_tan_len - 1)))
                coords.append(
                    prev_coord.add(App.Vector(_forward).multiply(mm_tan_len)))

            #zero radius or curve direction means no curve.  We're done
            if radius > 0.0:

                _left = App.Vector(_forward.y, -_forward.x, 0.0)
                seg_rad = central_angle / float(segments)

                prev_coord = coords[-1]

                radius_mm = radius * 304.80
                unit_delta = seg_rad * 0.01

                print('prev coord ', prev_coord)
                print('radius mm ', radius_mm)
                print('unit delta ', unit_delta)
                print('seg_rad ', seg_rad)
                print('segments ', segments)

                for _i in range(0, segments):

                    delta = float(_i + 1) * seg_rad

                    _dfw = App.Vector(_forward).multiply(math.sin(delta))
                    _dlt = App.Vector(_left).multiply(curve_dir *
                                                      (1 - math.cos(delta)))

                    coords.append(
                        prev_coord.add(_dfw.add(_dlt).multiply(radius_mm)))

                    print('next coord ', coords[-1])

            prev_geo = _geo
            prev_coord = coords[-1]
            prev_curve_tangent = curve_tangent

        print('COORDS: ', coords)
        return coords