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
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 }
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