def test_constrainToRevolution(self): """test_revolvePositive doc...""" for i in range(100): value = random.uniform(-2000.0, 5000.0) a = Angle(degrees=value) a.constrainToRevolution() self.assertLessEqual(a.degrees, 360.0) self.assertGreaterEqual(a.degrees, 0.0)
def test_radians(self): """test_radians doc...""" for i in range(100): value = random.uniform(-2000.0, 5000.0) a = Angle(radians=value) self.assertAlmostEqual(a.radians, value) for i in range(100): value = random.uniform(-2000.0, 5000.0) a = Angle() a.radians = value self.assertAlmostEqual(a.radians, value)
def test_degrees(self): """ doc... """ for i in range(100): value = random.uniform(-2000.0, 5000.0) a = Angle(degrees=value) self.assertAlmostEqual(a.degrees, value) for i in range(100): value = random.uniform(-2000.0, 5000.0) a = Angle() a.degrees = value self.assertAlmostEqual(a.degrees, value)
def test_differenceBetween(self): """test_differenceBetween doc...""" for i in range(1000): value = random.uniform(-2000.0, 5000.0) a = Angle(degrees=value) a.constrainToRevolution() value = random.uniform(-2000.0, 5000.0) b = Angle(degrees=value) b.constrainToRevolution() self.assertLessEqual(abs(a.differenceBetween(b).degrees), 180.0)
def _analyzeTrackSeries(self, series, trackway, sitemap): if len(series.tracks) < 2: return prev_track = series.tracks[0] for track in series.tracks[1:]: stride_line = LineSegment2D( start=prev_track.positionValue, end=track.positionValue) stride_angle = stride_line.angle abs_angle = Angle(degrees=prev_track.rotation) if not prev_track.left: local_angle = stride_angle.differenceBetween(abs_angle) else: local_angle = abs_angle.differenceBetween(stride_angle) has_field_measurements = not prev_track.hasImportFlag( ImportFlagsEnum.NO_FIELD_MEASUREMENTS ) if has_field_measurements: measuredRotation = prev_track.rotationMeasured difference = round(abs(measuredRotation - local_angle.degrees)) deviation = NumericUtils.roundToOrder( value=difference/prev_track.rotationUncertainty, orderOfMagnitude=-2) else: measuredRotation = '' difference = '' deviation = '' self._csv.createRow( uid=prev_track.uid, fingerprint=prev_track.fingerprint, difference=difference, deviation=deviation, localRotation=round(local_angle.degrees), measuredRotation=measuredRotation) prev_track = track
def angleBetween(self, position): """angleBetween doc...""" myLength = self.length posLength = position.length denom = myLength.raw * posLength.raw denomUnc = math.sqrt( myLength.rawUncertainty * myLength.rawUncertainty + posLength.rawUncertainty * posLength.rawUncertainty) if denom == 0.0: return Angle(radians=0.0, uncertainty=0.5 * math.pi) nom = self.x * position.x + self.y * position.y nomUnc = (abs(position.x) * self.xUnc + abs(self.x) * position.xUnc + abs(position.y) * self.yUnc + abs(self.y) * position.yUnc) / denom b = nom / denom bUnc = abs(1.0/denom)*nomUnc + \ abs(nom/math.pow(denom, 2))*denomUnc if NumericUtils.equivalent(b, 1.0): return Angle() try: if NumericUtils.equivalent(b, -1.0): a = math.pi else: a = math.acos(b) except Exception: print('[ERROR]: Unable to calculate angle between', b) return Angle() if NumericUtils.equivalent(a, math.pi): return Angle(radians=a, uncertainty=180.0) try: aUnc = abs(1.0 / math.sqrt(1.0 - b * b)) * bUnc except Exception: print('[ERROR]: Unable to calculate angle between uncertainty', b, a) return Angle() return Angle(radians=a, uncertainty=aUnc)
def _analyzeTrackSeries(self, series, trackway, sitemap): if len(series.tracks) < 2: # At least two tracks are required to make the comparison return for track in series.tracks: fieldAngle = Angle( degrees=track.rotationMeasured \ if track.rotationMeasured \ else 0.0) dataAngle = Angle(degrees=track.rotation) strideLine = StrideLine(track=track, series=series) if track.hidden or strideLine.pairTrack.hidden: continue try: strideLine.vector.normalize() except ZeroDivisionError: pair = strideLine.pairTrack self.logger.write([ '[ERROR]: Stride line was a zero length vector', 'TRACK: %s (%s, %s) [%s]' % ( track.fingerprint, NumericUtils.roundToSigFigs(track.x, 3), NumericUtils.roundToSigFigs(track.z, 3), track.uid), 'PAIRING: %s (%s, %s) [%s]' % ( pair.fingerprint, NumericUtils.roundToSigFigs(pair.x, 3), NumericUtils.roundToSigFigs(pair.z, 3), pair.uid) ]) continue axisAngle = strideLine.angle if track.left: fieldAngle.radians += axisAngle.radians else: fieldAngle.radians = axisAngle.radians - fieldAngle.radians # Adjust field angle into range [-180, 180] fieldAngle.constrainToRevolution() if fieldAngle.degrees > 180.0: fieldAngle.degrees -= 360.0 fieldAngleUnc = Angle(degrees=5.0) fieldAngleUnc.radians += \ 0.03/math.sqrt(1.0 - math.pow(strideLine.vector.x, 2)) fieldDeg = NumericUtils.toValueUncertainty( value=fieldAngle.degrees, uncertainty=fieldAngleUnc.degrees) # Adjust data angle into range [-180, 180] dataAngle.constrainToRevolution() if dataAngle.degrees > 180.0: dataAngle.degrees -= 360.0 dataAngleUnc = Angle(degrees=track.rotationUncertainty) dataDeg = NumericUtils.toValueUncertainty( value=dataAngle.degrees, uncertainty=dataAngleUnc.degrees) angle1 = Angle(degrees=dataDeg.value) angle2 = Angle(degrees=fieldDeg.value) # fill color for the disks to be added to the map are based on # diffDeg diffDeg = NumericUtils.toValueUncertainty( value=angle1.differenceBetween(angle2).degrees, uncertainty=min(90.0, math.sqrt( math.pow(dataAngleUnc.degrees, 2) + math.pow(fieldAngleUnc.degrees, 2))) ) self._diffs.append(diffDeg.value) deviation = diffDeg.value/diffDeg.uncertainty self.deviations[track.uid] = diffDeg # for now, convert +/- 180 headings to 0-360, using e and m # comment the next four lines toggle comments for entered and # measured below to revert e = dataDeg.value m = fieldDeg.value if e < 0.0: e += 360.0 if m < 0.0: m += 360.0 data = dict( uid=track.uid, fingerprint=track.fingerprint, entered=str(e), measured=str(m), delta=abs(diffDeg.value), deviation=deviation, relative=NumericUtils.roundToOrder(track.rotationMeasured, -2), axis=NumericUtils.roundToOrder(axisAngle.degrees, -2), axisPairing='NEXT' if strideLine.isNext else 'PREV') self._csv.createRow(**data) data['track'] = track self._data.append(data) # draw the stride line pointer for reference in green self._currentDrawing.use( 'pointer', (track.x, track.z), scene=True, rotation=axisAngle.degrees, stroke_width=1, scale=0.5, stroke='green') # indicate in blue the map-derived estimate of track rotation self._currentDrawing.use( 'pointer', (track.x, track.z), scene=True, rotation=dataDeg.value, stroke_width=1, stroke='blue') # add the measured (spreadsheet) estimate of rotation self._currentDrawing.use( 'pointer', (track.x, track.z), scene=True, rotation=fieldDeg.value, stroke_width=1, stroke='red') # place a translucent disk of radius proportional to the difference # in degrees radius = 100.0*diffDeg.value/180.0 self._currentDrawing.circle( (track.x, track.z), radius, scene=True, fill='red', stroke_width=0.5, stroke='red', fill_opacity='0.5')
def closestPointOnLine(self, point, contained =True): """ Finds the closest point on a line to the specified point using the formulae discussed in the "another formula" section of: http://en.m.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Another_formula """ length = self.length if not length: raise ValueError('Cannot calculate point. Invalid line segment.') s = self.start e = self.end deltaX = e.x - s.x deltaY = e.y - s.y rotate = False slope = 0.0 slopeUnc = 0.0 try: slope = deltaY/deltaX slopeUnc = abs(1.0/deltaX)*(s.yUnc + e.yUnc) + abs(slope/deltaX)*(s.xUnc + e.xUnc) except Exception: rotate = True if rotate or (abs(slope) > 1.0 and abs(slopeUnc/slope) > 0.5): a = Angle(degrees=20.0) line = self.clone() line.rotate(a, self.start) p = point.clone() p.rotate(a, self.start) result = line.closestPointOnLine(p, contained=contained) if result is None: return result a.degrees = -20.0 result.rotate(a, self.start) return result intercept = s.y - slope*s.x denom = slope*slope + 1.0 numer = point.x + slope*(point.y - intercept) x = numer/denom y = (slope*numer)/denom + intercept if contained: # Check to see if point is between start and end values xRange = sorted([self.start.x, self.end.x]) yRange = sorted([self.start.y, self.end.y]) eps = 1e-8 xMin = x - eps xMax = x + eps yMin = y - eps yMax = y + eps if xRange[1] < xMin or xMax < xRange[0] or yRange[1] < yMin or yMax < yRange[0]: return None pos = PositionValue2D(x=x, y=y) startDist = self.start.distanceTo(pos) endDist = self.end.distanceTo(pos) xUnc = startDist.raw/length.raw*self.start.xUnc + endDist.raw/length.raw*self.end.xUnc pos.xUnc = math.sqrt(xUnc**2 + point.xUnc**2) yUnc = startDist.raw/length.raw*self.start.yUnc + endDist.raw/length.raw*self.end.yUnc pos.yUnc = math.sqrt(yUnc**2 + point.yUnc**2) return pos