def dist_traveled(self, stop, old_dist_traveled): if old_dist_traveled and self._xdist: # Case 1: we have in the original data shape_dist_traveled # We need to remap from the old scale to the new meter scale return self._xdist.interpolate(old_dist_traveled) else: # Case 2: we do not have original shape_dist_traveled # We need to determine ourselves where in the shape we lie # TODO Implement a cache, this can be slow for lots of trips # and the result is the same for the same pattern # Check the cache first cache_entry = self._cache_cursor.next_entry(stop) if cache_entry is not None: self._cache_cursor = cache_entry self._cache_hit += 1 return cache_entry.distance() min_dist = 1e20 best_i = self._istart best_dist = 0 for i in range(self._istart, len(self._shape.points) - 1): a = self._shape.points[i] b = self._shape.points[i + 1] dist, pdist = orthodromic_seg_distance(stop, a, b) newdist = a.shape_dist_traveled + pdist howfar = newdist - self._distance # Add a slight "cone" offset. There are pathological # cases with backtracking shapes where the best distance # is slightly better way further (for eg 0.01m) than at # the starting point (for eg 0.02m). In that case we should # obviously keep the first point instead of moving too fast # to the shape end. That offset should help for some cases. dist += howfar * self.K if dist < min_dist: min_dist = dist best_i = i best_dist = newdist if best_dist > self._distance: self._distance = best_dist else: delta = self._distance - best_dist if delta > 10: # This is harmless if the backtracking distance is small. # We have lots of false positive (<<1m) due to rounding errors. logger.warn( "Backtracking of %f m detected in shape %s for stop %s (%s) (%f,%f) at distance %f < %f m on segment #[%d-%d]" % (delta, self._shape.shape_id, stop.stop_id, stop.stop_name, stop.stop_lat, stop.stop_lon, best_dist, self._distance, best_i, best_i + 1)) self._istart = best_i self._cache_miss += 1 self._cache_cursor = self._cache_cursor.insert( stop, self._distance) return self._distance
def test_seg_distance(self): a = SimplePoint(0, 0) daaa, daaa2 = orthodromic_seg_distance(a, a, a) self.assertAlmostEqual(daaa, 0.0, 3) self.assertAlmostEqual(daaa2, 0.0, 3) b = SimplePoint(1, 0) daab, daab2 = orthodromic_seg_distance(a, a, b) self.assertAlmostEqual(daab, 0.0, 3) self.assertAlmostEqual(daab2, 0.0, 3) dbab, dbab2 = orthodromic_seg_distance(b, a, b) self.assertAlmostEqual(dbab, 0.0, 3) self.assertAlmostEqual(dbab2 / 60, self._NAUTICAL_MILE, 2) c = SimplePoint(0.5, 0) dcab, dcab2 = orthodromic_seg_distance(c, a, b) self.assertAlmostEqual(dcab, 0.0, 3) self.assertAlmostEqual(dcab2 / 60, self._NAUTICAL_MILE / 2.0, 3) d = SimplePoint(-1, 0) ddab, ddab2 = orthodromic_seg_distance(d, a, b) self.assertAlmostEqual(ddab / 60, self._NAUTICAL_MILE, 2) print(ddab2) self.assertAlmostEqual(ddab2, 0, 2) e = SimplePoint(2, 0) deab, deab2 = orthodromic_seg_distance(e, a, b) self.assertAlmostEqual(deab / 60, self._NAUTICAL_MILE, 2) self.assertAlmostEqual(deab2 / 60, self._NAUTICAL_MILE, 2) f = SimplePoint(0.01, 1) dfab, dfab2 = orthodromic_seg_distance(f, a, b) self.assertAlmostEqual(dfab / 60, self._NAUTICAL_MILE, 2) self.assertAlmostEqual(dfab2 / 60, self._NAUTICAL_MILE * 0.01, 2) g = SimplePoint(1, 1) h = SimplePoint(0.5, 0.5) dhag, dhag2 = orthodromic_seg_distance(h, a, g) self.assertAlmostEqual(dhag, 0, 3) self.assertAlmostEqual(dhag2 / 60, self._NAUTICAL_MILE / 2 * math.sqrt(2), 0) # Please note that the following is true only because # the distance is an approximation on the equirectangular projection. dbag, dbag2 = orthodromic_seg_distance(b, a, g) self.assertAlmostEqual(dbag / 60 * math.sqrt(2), self._NAUTICAL_MILE, 0) self.assertAlmostEqual(dbag2 / 60, self._NAUTICAL_MILE / 2 * math.sqrt(2), 0)
def dist_traveled(self, stop, old_dist_traveled): if old_dist_traveled and self._xdist: # Case 1: we have in the original data shape_dist_traveled # We need to remap from the old scale to the new meter scale return self._xdist.interpolate(old_dist_traveled) else: # Case 2: we do not have original shape_dist_traveled # We need to determine ourselves where in the shape we lie # TODO Implement a cache, this can be slow for lots of trips # and the result is the same for the same pattern # Check the cache first cache_entry = self._cache_cursor.next_entry(stop) if cache_entry is not None: self._cache_cursor = cache_entry self._cache_hit += 1 return cache_entry.distance() min_dist = 1e20 best_i = self._istart best_dist = 0 for i in range(self._istart, len(self._shape.points) - 1): a = self._shape.points[i] b = self._shape.points[i+1] dist, pdist = orthodromic_seg_distance(stop, a, b) newdist = a.shape_dist_traveled + pdist howfar = newdist - self._distance # Add a slight "cone" offset. There are pathological # cases with backtracking shapes where the best distance # is slightly better way further (for eg 0.01m) than at # the starting point (for eg 0.02m). In that case we should # obviously keep the first point instead of moving too fast # to the shape end. That offset should help for some cases. dist += howfar * self.K if dist < min_dist: min_dist = dist best_i = i best_dist = newdist if best_dist > self._distance: self._distance = best_dist else: delta = self._distance - best_dist if delta > 10: # This is harmless if the backtracking distance is small. # We have lots of false positive (<<1m) due to rounding errors. logger.warn("Backtracking of %f m detected in shape %s for stop %s (%s) (%f,%f) at distance %f < %f m on segment #[%d-%d]" % ( delta, self._shape.shape_id, stop.stop_id, stop.stop_name, stop.stop_lat, stop.stop_lon, best_dist, self._distance, best_i, best_i+1)) self._istart = best_i self._cache_miss += 1 self._cache_cursor = self._cache_cursor.insert(stop, self._distance) return self._distance
def test_seg_distance(self): a = SimplePoint(0, 0) daaa, daaa2 = orthodromic_seg_distance(a, a, a) self.assertAlmostEqual(daaa, 0.0, 3) self.assertAlmostEqual(daaa2, 0.0, 3) b = SimplePoint(1, 0) daab, daab2 = orthodromic_seg_distance(a, a, b) self.assertAlmostEqual(daab, 0.0, 3) self.assertAlmostEqual(daab2, 0.0, 3) dbab, dbab2 = orthodromic_seg_distance(b, a, b) self.assertAlmostEqual(dbab, 0.0, 3) self.assertAlmostEqual(dbab2 / 60, self._NAUTICAL_MILE, 2) c = SimplePoint(0.5, 0) dcab, dcab2 = orthodromic_seg_distance(c, a, b) self.assertAlmostEqual(dcab, 0.0, 3) self.assertAlmostEqual(dcab2 / 60, self._NAUTICAL_MILE / 2.0, 3) d = SimplePoint(-1, 0) ddab, ddab2 = orthodromic_seg_distance(d, a, b) self.assertAlmostEqual(ddab / 60, self._NAUTICAL_MILE, 2) print(ddab2) self.assertAlmostEqual(ddab2, 0, 2) e = SimplePoint(2, 0) deab, deab2 = orthodromic_seg_distance(e, a, b) self.assertAlmostEqual(deab / 60, self._NAUTICAL_MILE, 2) self.assertAlmostEqual(deab2 / 60, self._NAUTICAL_MILE, 2) f = SimplePoint(0.01, 1) dfab, dfab2 = orthodromic_seg_distance(f, a, b) self.assertAlmostEqual(dfab / 60, self._NAUTICAL_MILE, 2) self.assertAlmostEqual(dfab2 / 60, self._NAUTICAL_MILE * 0.01, 2) g = SimplePoint(1, 1) h = SimplePoint(0.5, 0.5) dhag, dhag2 = orthodromic_seg_distance(h, a, g) self.assertAlmostEqual(dhag, 0, 3) self.assertAlmostEqual(dhag2 / 60, self._NAUTICAL_MILE / 2 * math.sqrt(2), 0) # Please note that the following is true only because # the distance is an approximation on the equirectangular projection. dbag, dbag2 = orthodromic_seg_distance(b, a, g) self.assertAlmostEqual(dbag / 60 * math.sqrt(2), self._NAUTICAL_MILE, 0) self.assertAlmostEqual(dbag2 / 60, self._NAUTICAL_MILE / 2 * math.sqrt(2), 0)