Example #1
0
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
    }
    def discretize_geometry(self):
        '''
        Discretizes the alignment geometry to a series of vector points
        '''

        interval = self.Object.Seg_Value
        interval_type = self.Object.Method
        geometry = self.geometry['geometry']
        points = [[self.geometry['meta']['Start']]]
        last_curve = None

        #discretize each arc in the geometry list,
        #store each point set as a sublist in the main points list
        for curve in geometry:

            if not curve:
                continue

            if curve['Type'] == 'arc':
                points.append(Arc.get_points(curve, interval, interval_type))

            #if curve['Type'] == 'line':
            #    points.append([curve['Start'], curve['End']])

            last_curve = curve

        #store the last point of the first geometry for the next iteration
        _prev = points[0][-1]
        result = points[0]

        if not (_prev and result):
            return None

        #iterate the point sets, adding them to the result set
        #and eliminating any duplicate points
        for item in points[1:]:

            if _prev.sub(item[0]).Length < 0.0001:
                result.extend(item[1:])
            else:
                result.extend(item)

            _prev = item[-1]

        last_tangent = abs(self.geometry['meta']['Length'] -
                           last_curve['InternalStation'][1])

        if not Support.within_tolerance(last_tangent):
            _vec = Support.vector_from_angle(
                last_curve['BearingOut']).multiply(last_tangent)
            last_point = result[-1]

            result.append(last_point.add(_vec))

        #print('\n<><><><><>\n', result)
        return result
    def validate_coordinates(self):
        '''
        Iterate the geometry, testing for incomplete / incorrect station / coordinate values
        Fix them where possible, error if values cannot be resolved
        '''

        #calculate distance bewteen curve start and end using
        #internal station and coordinate vectors

        _datum = self.geometry['meta']
        _geo_data = self.geometry['geometry']

        _prev_geo = {
            'End': _datum['Start'],
            'InternalStation': (0.0, 0.0),
            'StartStation': _datum['StartStation'],
            'Length': 0.0
        }

        for _geo in _geo_data:

            if not _geo:
                continue
            #get the vector between the two gemetries and the station distance
            _vector = _geo['Start'].sub(_prev_geo['End'])
            _sta_len = abs(_geo['InternalStation'][0] -
                           _prev_geo['InternalStation'][1])

            #calculate the difference between the vector length and station distance
            #in document units
            _delta = (_vector.Length - _sta_len) / Units.scale_factor()

            #if the stationing / coordinates are out of tolerance,
            #determine if the error is with the coordinate vector or station
            if not Support.within_tolerance(_delta):
                bearing_angle = Support.get_bearing(_vector)

                #if the coordinate vector bearing matches, fix the station
                if Support.within_tolerance(bearing_angle, _geo['BearingIn']):
                    _geo['InternalStation'] = (
                        _prev_geo['InternalStation'][1] + _vector.Length,
                        _geo['InternalStation'][0])

                    _geo['StartStation'] = _prev_geo['StartStation'] + \
                                           _prev_geo['Length'] / Units.scale_factor() + \
                                           _vector.Length / Units.scale_factor()

                #otherwise, fix the coordinate
                else:
                    _bearing_vector = Support.vector_from_angle(
                        _geo['BearingIn'])
                    _bearing_vector.multiply(_sta_len)

                    _geo['Start'] = _prev_geo['End'].add(_bearing_vector)

            _prev_geo = _geo
Example #4
0
def get_parameters(line):
    '''
    Return a fully-defined line
    '''

    _coord_truth = [not line.get('Start') is None, not line.get('End') is None]
    _param_truth = [
        not line.get('BearingIn') is None, not line.get('Length') is None
    ]

    #both coordinates defined
    _case_one = all(_coord_truth)

    #only one coordinate defined, plus both length and bearing
    _case_two = any(_coord_truth) and not all(_coord_truth) and all(
        _param_truth)

    if _case_one:

        line_vec = line['End'].sub(line['Start'])
        _bearing = Support.get_bearing(line_vec)
        _length = line_vec.Length

        #test for missing parameters, preserving the existing ones
        if line.get('BearingIn'):
            if Support.within_tolerance(line['BearingIn'], _bearing):
                _bearing = line['BearingIn']

        line['BearingIn'] = line['BearingOut'] = _bearing

        if line.get('Length'):
            if Support.within_tolerance(line['Length'], _length):
                _length = line['Length']

        line['Length'] = _length

    elif _case_two:

        _vec = Support.vector_from_angle(line['BearingIn']).multiply(
            line['Length'])

        if line.get('Start'):
            line['End'] = line['Start'].add(_vec)
        else:
            line['Start'] = line['End'].add(_vec)

    else:
        print('Unable to calculate line parametters')

    result = None

    if _case_one or _case_two:
        result = {**{'Type': 'line'}, **line}

    return result
Example #5
0
def run_test(arc, comp, excludes):

    import copy
    dct = copy.deepcopy(arc)

    if excludes:
        for _exclude in excludes:
            dct[_exclude] = None

    result = convert_units(get_parameters(convert_units(dct)), True)

    print('----------- Comparison errors: ------------- \n')
    print('Exclusions: ', excludes)

    for _k, _v in comp.items():

        _w = result[_k]
        _x = _v

        if isinstance(_v, App.Vector):
            _x = _v.Length
            _w = _w.Length

        if not Support.within_tolerance(_x, _w):
            print('Mismatch on %s: %f (%f)' % (_k, _w, _x))

    return result
    def validate_alignment(self):
        '''
        Ensure the alignment geometry is continuous.
        Any discontinuities (gaps between end / start coordinates)
        must be filled by a completely defined line
        '''

        _prev_coord = self.geometry['meta']['Start']
        _geo_list = []

        for _geo in self.geometry['geometry']:

            if not _geo:
                continue

            _coord = _geo['Start']

            if not Support.within_tolerance(_coord.Length, _prev_coord.Length):

                #build the line using the provided parameters and add it
                _geo_list.append(
                    Line.get_parameters({
                        'Start': App.Vector(_prev_coord),
                        'End': App.Vector(_coord),
                        'BearingIn': _geo['BearingIn'],
                        'BearingOut': _geo['BearingOut'],
                    }))

            _geo_list.append(_geo)
            _prev_coord = _geo['End']

        self.geometry['geometry'] = _geo_list
Example #7
0
def get_lengths(arc, mat):
    '''
    Get needed parameters for arc calculation
    from the user-defined arc and the calculated vector matrix
    '''

    #[0,1] = Radius; [2, 3] = Tangent, [4] = Middle, [5] = Chord
    lengths = mat.diagonal().A[0]

    params = [
        arc.get('Radius'),
        arc.get('Tangent'),
        arc.get('Middle'),
        arc.get('Chord'),
        arc.get('Delta')
    ]

    for _i in range(0, 2):

        #get two consecutive elements, saving only if they're valid
        _s = [_v for _v in lengths[_i * 2:(_i + 1) * 2] if _v]

        #skip the rest if not defined, we'll use the user values
        if not _s:
            continue

        #if both were calculated and they aren't the same, quit
        if all(_s) and not Support.within_tolerance(_s):
            return None

        if _s[0] and Support.within_tolerance(_s[0], params[_i]):
            continue

        params[_i] = _s[0]

    #test middle and chord, if no user-defined value or out-of-tolerance, use calculated
    for _i in range(4, 6):

        if lengths[_i] and Support.within_tolerance(lengths[_i],
                                                    params[_i - 2]):
            continue

        params[_i - 2] = lengths[_i]

    return {'Radius': params[0], 'Tangent': params[1], 'Chord': params[3]}
Example #8
0
def get_scalar_matrix(vecs):
    '''
    Calculate the square matrix of scalars
    for the provided vectors
    '''
    #ensure list is a list of lists (not vectors)
    #and create the matrix
    mat_list = [list(_v) if _v else [0, 0, 0] for _v in vecs]
    rot_list = [0.0] * 7

    #get rotation direction for vector bearings
    for _i in range(0, 6):
        rot_list[_i] = Support.get_rotation(C.UP, vecs[_i])

    mat_list.append(list(C.UP))

    mat = numpy.matrix(mat_list)
    result = mat * mat.T

    #abort for non-square matrices
    if result.shape[0] != result.shape[1] != 6:
        return None

    #calculate the magnitudes first (minus the UP vector)
    for _i in range(0, 6):
        result.A[_i][_i] = math.sqrt(result.A[_i][_i])

    #calculate the delta for the lower left side
    for _i in range(0, 7):
        _d1 = result.A[_i][_i]

        for _j in range(0, _i):
            _denom = _d1 * result.A[_j][_j]
            _n = result.A[_i][_j]

            _angle = None

            if not (any([math.isnan(_v)
                         for _v in [_denom, _n]]) or _denom == 0.0):
                _angle = math.acos(_n / _denom)

            #compute the arc central angle for all but the last row
            if _angle:
                if _i < 6:
                    _angle = _GEO.FUNC[_i][_j](_angle)
                else:
                    _angle *= rot_list[_j]

                    if _angle < 0.0:
                        _angle += C.TWO_PI

            result.A[_i][_j] = _angle

    #lower left half contains angles, diagonal contains scalars
    return result
Example #9
0
def get_rotation(arc, vecs):
    '''
    Determine the dirction of rotation
    '''
    v1 = [_v for _v in vecs[0:3] if _v and _v != App.Vector()]
    v2 = [_v for _v in vecs[3:] if _v and _v != App.Vector()]

    if not (v1 and v2):
        return {'Direction': arc.get('Direction')}

    return {'Direction': Support.get_rotation(v1[0], v2[1])}
Example #10
0
def get_missing_parameters(arc, new_arc):
    '''
    Calculate any missing parameters from the original arc
    using the values from the new arc.

    These include:
     - Chord
     - Middle Ordinate
     - Tangent
     - Length
     - External distance
    '''

    #by this point, missing radius / delta is a problem
    if new_arc.get('Radius') is None or new_arc.get('Delta') is None:
        return None

    #pre-calculate values and fill in remaining parameters
    radius = new_arc['Radius']
    delta = new_arc['Delta']
    half_delta = delta / 2.0

    new_arc['Length'] = radius * delta
    new_arc['External'] = radius * ((1.0 / math.cos(half_delta)) - 1.0)
    new_arc['MiddleOrdinate'] = radius * (1.0 - math.cos(half_delta))

    if not new_arc.get('Tangent'):
        new_arc['Tangent'] = radius * math.tan(half_delta)

    if not new_arc.get('Chord'):
        new_arc['Chord'] = 2.0 * radius * math.sin(half_delta)

    #quality-check - ensure everything is defined and default to existing where within tolerance
    _keys = ['Chord', 'MiddleOrdinate', 'Tangent', 'Length', 'External']

    existing_vals = [arc.get(_k) for _k in _keys]
    new_vals = [new_arc.get(_k) for _k in _keys]

    vals = {}

    for _i, _v in enumerate(_keys):

        vals[_v] = existing_vals[_i]

        #if values are close enough, then keep existing
        if Support.within_tolerance(vals[_v], new_vals[_i]):
            continue

        #out of tolerance or existing is None - use the calculated value
        vals[_v] = new_vals[_i]

    return vals
    def validate_stationing(self):
        '''
        Iterate the geometry, calculating the internal start station based on the actual station
        and storing it in an 'InternalStation' parameter tuple for the start and end of the curve
        '''

        prev_station = self.geometry['meta'].get('StartStation')
        prev_coord = self.geometry['meta'].get('Start')

        if not prev_coord or not prev_station:
            print('Unable to validate alignment stationing')
            return

        for _geo in self.geometry['geometry']:

            if not _geo:
                continue

            _geo['InternalStation'] = None
            geo_station = _geo.get('StartStation')
            geo_coord = _geo['Start']

            #if no station is provided, try to infer it from the start coordinate
            #and the previous station
            if geo_station is None:
                geo_station = prev_station

                if not geo_coord:
                    geo_coord = prev_coord

                delta = geo_coord.sub(prev_coord).Length

                if not Support.within_tolerance(delta):
                    geo_station += delta / Units.scale_factor()

                _geo['StartStation'] = geo_station

            prev_coord = _geo['End']
            prev_station = _geo[
                'StartStation'] + _geo['Length'] / Units.scale_factor()

            int_sta = self._get_internal_station(geo_station)

            _geo['InternalStation'] = (int_sta, int_sta + _geo['Length'])
Example #12
0
def get_coordinates(arc, points):
    '''
    Fill in any missing coordinates using arc parameters
    '''

    vectors = {}

    for _k, _v in arc['Bearings'].items():
        vectors[_k] = [Support.vector_from_angle(_x) for _x in _v]

    _start = points[0]
    _end = points[1]
    _center = points[2]
    _pi = points[3]

    _vr = vectors['Radius'][0].multiply(arc['Radius'])
    _vt = vectors['Tangent'][0].multiply(arc['Tangent'])
    _vc = vectors['Internal'][1].multiply(arc['Chord'])

    if not _start:

        if _pi:
            _start = _pi.sub(_vt)

        elif _center:
            _start = _center.add(_vr)

        elif _end:
            _start = _end.sub(_vc)

    if not _start:
        return None

    if not _pi:
        _pi = _start.add(_vt)

    if not _center:
        _center = _start.sub(_vr)

    if not _end:
        _end = _start.add(_vc)

    return {'Start': _start, 'Center': _center, 'End': _end, 'PI': _pi}
    def validate_bearings(self):
        '''
        Validate the bearings between geometry, ensuring they are equal
        '''

        geo_data = self.geometry['geometry']

        if len(geo_data) < 2:
            return True

        if geo_data[0] is None:
            return False

        prev_bearing = geo_data[0]['BearingOut']

        for _geo in geo_data[1:]:

            if not _geo:
                continue

            _b = _geo.get('BearingIn')

            if _b is None:
                self.errors.append(
                    'Invalid bearings ({0:.4f}, {1:.4f}) at curve {2}'.format(
                        prev_bearing, _b, _geo))
                return False

            if not Support.within_tolerance(_b, prev_bearing):
                self.errors.append(
                    'Bearing mismatch ({0:.4f}, {1:.4f}) at curve {2}'.format(
                        prev_bearing, _b, _geo))
                return False

            prev_bearing = _geo.get('BearingOut')

        return True
 def test_vector_from_angle(self):
     
     self.assertIsNone(
         Support.vector_from_angle('1fdsa.0'), 
         'Supprt.vector_from_angle() fails non-float test'
     )
Example #15
0
def get_parameters(arc):

    #Vector order:
    #Radius in / out, Tangent in / out, Middle, and Chord
    points = [
        arc.get('Start'),
        arc.get('End'),
        arc.get('Center'),
        arc.get('PI')
    ]

    point_count = len([_v for _v in points if _v])

    #define the curve start at the origin if none is provided
    if point_count == 0:
        points[0] = App.Vector()

    vecs = [
        Support.safe_sub(arc.get('Start'), arc.get('Center'), True),
        Support.safe_sub(arc.get('End'), arc.get('Center'), True),
        Support.safe_sub(arc.get('PI'), arc.get('Start'), True),
        Support.safe_sub(arc.get('End'), arc.get('PI'), True),
        Support.safe_sub(arc.get('PI'), arc.get('Center'), True),
        Support.safe_sub(arc.get('End'), arc.get('Start'), True)
    ]

    result = {'Type': 'arc'}

    mat = get_scalar_matrix(vecs)
    _p = get_lengths(arc, mat)

    if not _p:
        print(
            'Invalid curve definition: cannot determine radius / tangent lengths'
        )
        return None

    result.update(_p)
    _p = get_delta(arc, mat)

    if not _p:
        print('Invalid curve definition: cannot determine central angle')
        return None

    result.update(_p)
    _p = get_rotation(arc, vecs)

    if not _p:
        print('Invalid curve definition: cannot determine curve direction')
        return None

    result.update(_p)
    _p = get_bearings(arc, mat, result['Delta'], result['Direction'])

    if not _p:
        print('Invalid curve definition: cannot determine curve bearings')
        return None

    result.update(_p)
    _p = get_missing_parameters(result, result)

    if not _p:
        print('Invalid curve definition: cannot calculate all parameters')
        return None

    result.update(_p)
    _p = get_coordinates(result, points)

    if not _p:
        print('Invalid curve definition: cannot calculate coordinates')
        return None

    result.update(_p)

    #get rid of the Bearings dict since we're done using it
    result.pop('Bearings')

    #merge the result with the original dict to preserve other values
    return {**arc, **result}
    def validate_datum(self):
        '''
        Ensure the datum is valid, assuming 0+00 / (0,0,0) for station and coordinate
        where none is suplpied and it cannot be inferred fromt the starting geometry
        '''
        _datum = self.geometry['meta']
        _geo = self.geometry['geometry'][0]

        if not _geo or not _datum:
            print('Unable to validate alignment datum')
            return

        _datum_truth = [
            not _datum.get('StartStation') is None,
            not _datum.get('Start') is None
        ]

        _geo_truth = [
            not _geo.get('StartStation') is None, not _geo.get('Start') is None
        ]

        #----------------------------
        #CASE 0
        #----------------------------
        #both defined?  nothing to do
        if all(_datum_truth):
            return

        _geo_station = 0
        _geo_start = App.Vector()

        if _geo_truth[0]:
            _geo_station = _geo['StartStation']

        if _geo_truth[1]:
            _geo_start = _geo['Start']

        #---------------------
        #CASE 1
        #---------------------
        #no datum defined?  use initial geometry or zero defaults
        if not any(_datum_truth):

            _datum['StartStation'] = _geo_station
            _datum['Start'] = _geo_start
            return

        #--------------------
        #CASE 2
        #--------------------
        #station defined?
        #if the geometry has a station and coordinate, project the start coordinate
        if _datum['StartStation']:

            _datum['Start'] = _geo_start

            #assume geometry start if no geometry station
            if not _geo_truth[0]:
                return

            #scale the distance to the system units
            delta = _geo_station - _datum['StartStation']

            #cutoff if error is below tolerance
            if not Support.within_tolerance(delta):
                delta *= Units.scale_factor()
            else:
                delta = 0.0

            #assume geometry start if station delta is zero
            if delta:

                #calcualte the start based on station delta
                _datum['Start'] = _datum['Start'].sub(
                    Support.vector_from_angle(
                        _geo['BearingIn']).multiply(delta))

            return

        #---------------------
        #CASE 3
        #---------------------
        #datum start coordinate is defined
        #if the geometry has station and coordinate, project the start station
        _datum['StartStation'] = _geo_station

        #assume geometry station if no geometry start
        if _geo_truth[1]:

            #scale the length to the document units
            delta = _geo_start.sub(
                _datum['Start']).Length / Units.scale_factor()

            _datum['StartStation'] -= delta