2016-09-06 52 views
1

我有“显著变化”筛选GeoLocationProvider(实现IObservable<System.Device.Location.GeoCoordinate>,输出每x毫秒的当前位置。使用反应扩展到对观察到的流

现在我想用RX阅读全部GPS坐标和只有当它是显著通知的位置改变的对象的用户(例如 - 行驶距离> 10米)

“主”代码

// [...] 
IObservable<GeoCoordinate> locationProvider = new GeoLocationProvider(); 
LocationFeed locationFeed = new LocationFeed(locationProvider); 

// register any interested observers on the locationFeed. 
ConsoleLocationReporter c1 = new ConsoleLocationReporter("reporter0001"); 
locationFeed.Subscribe(c1); 

LocationFeed实现看起来像这样:

using System; 
using System.Device.Location; 
using System.Reactive.Subjects; 

namespace My.Namespace.Movement 
{ 
    public class LocationFeed : ISubject<GeoCoordinate>, IDisposable 
    { 
     private readonly IDisposable _subscription; 
     private readonly Subject<GeoCoordinate> _subject; 

     public LocationFeed(IObservable<GeoCoordinate> observableSource) 
     { 
      _subject = new Subject<GeoCoordinate>(); 
      _subscription = observableSource.Subscribe(_subject); // TODO: Add logic to filter to only significant movement changes (> 10m) 
     } 

     public void Dispose() 
     { 
      _subscription?.Dispose(); 
      _subject?.Dispose(); 
     } 

     public void OnNext(GeoCoordinate value) 
     { 
      _subject.OnNext(value); 
     } 

     public void OnError(Exception error) 
     { 
      _subject.OnError(error); 
     } 

     public void OnCompleted() 
     { 
      _subject.OnCompleted(); 
     } 

     public IDisposable Subscribe(IObserver<GeoCoordinate> observer) 
     { 
      return _subject.Subscribe(observer); 
     } 
    } 
} 

问题1:会有地理座标提供了一种方法c1.DistanceTo(c2)来计算两个坐标之间的距离。我只想报告(发布)新的GeoCoordinates,如果阈值与上次推送的相比大于x。我如何实现这一目标?

问题2:是否使用主题OK和我实现ISubject的方式?我不想在我的“主”代码中添加所有接线,并将其全部移到单独的类中。

+0

如果你想有一个完整的答案,我很乐意进一步提供帮助,但可能您提供测试以显示您想要的内容以及缺少的“GeoCoordinate”类 –

+0

我同意Lee的观点 - 不要实现您自己的实现Rx接口的类。这样做只会产生不好的结果。 – Enigmativity

回答

4

我强烈建议不实施ISubject<T>(或者对于这个问题IObservable<T>IObserver<T>)。相反,尝试组合现有的工厂和类型,然后将它们暴露为“具有”关系,而不是“是”关系。

正如你所看到的你的LocationFeed纯粹是对observableSource参数的包装,所以似乎没有解决任何问题。我会建议删除它。

至于你的问题贴,一个解决方案是使用尺寸2和1。

IObservable<GeoCoordinate> locationProvider = new GeoLocationProvider(); 

locationProvider 
    .Buffer(2,1) 
    .Where(buffer=>buffer[0].DistanceTo(buffer[1]) > 10) 
    .Select(buffer=>buffer[1]) 
    .Subscribe(
     pos => Console.WriteLine(pos), 
     ex => { }, 
     () => {}); 

或步长的缓冲区,你可以使用Scan

IObservable<GeoCoordinate> locationProvider = new GeoLocationProvider(); 

locationProvider 
    .Scan(Tuple.Create(GeoCoordinate.Zero,GeoCoordinate.Zero), (acc, cur)=>Tuple.Create(acc.Item2, cur)) 
    .Where(pair=>pair.Item1.DistanceTo(pair.Item2) > 10) 
    .Select(pair=>pair.Item2) 
    .Subscribe(
     pos => Console.WriteLine(pos), 
     ex => { }, 
     () => {}); 

我不知道你的要求是什么产生的第一个价值。应该发布还是不发布?

编辑: 这里是(使用Point类型)的测试溶液时,“显著”变化发生时将推Unit。如果这不是正是你想要的,它应该是足以让你鼓捣得到你真正想要的

void Main() 
{ 
    var zero = new System.Drawing.Point(0,0); 
    var fenceDistance = 10; 

    var scheduler = new TestScheduler(); 
    var source = scheduler.CreateColdObservable(
     ReactiveTest.OnNext(1, new System.Drawing.Point(0,0)), 
     ReactiveTest.OnNext(2, new System.Drawing.Point(0,9)), //Not far enough 
     ReactiveTest.OnNext(3, new System.Drawing.Point(0,10)), //Touches the fence 
     ReactiveTest.OnNext(4, new System.Drawing.Point(0,15)), //Not far enough 
     ReactiveTest.OnNext(5, new System.Drawing.Point(0,40)) //Breaches the fence   
     ); 

    var observer = scheduler.CreateObserver<Unit>(); 

    source 
     .Scan(Tuple.Create(zero, zero), (acc, cur) => 
     { 
      if (DistanceBetween(acc.Item1, cur) >= fenceDistance) 
      { 
       return Tuple.Create(cur, cur); 
      } 
      else 
      { 
       return Tuple.Create(acc.Item1, cur); 
      } 
     }) 
     .Where(pair => pair.Item1 == pair.Item2) 
     .Select(pair => Unit.Default) 
     .Subscribe(observer); 


    scheduler.Start(); 

    ReactiveAssert.AreElementsEqual(new[] { 
     ReactiveTest.OnNext(1, Unit.Default), 
     ReactiveTest.OnNext(3, Unit.Default), 
     ReactiveTest.OnNext(5, Unit.Default) 
    },observer.Messages); 

} 

// Define other methods and classes here 
public static double DistanceBetween(System.Drawing.Point a, System.Drawing.Point b) 
{ 
    var xDelta = a.X -b.X; 
    var yDelta = a.Y - b.Y; 

    var distanceSqr = (xDelta * xDelta) + (yDelta * yDelta); 
    return Math.Sqrt(distanceSqr); 
} 
+0

您可能需要调整扫描解决方案以符合“如果阈值与最后一次推送相比较”的要求,即仅当新值远离“acc”时才更新“acc”。否则,行走的人可能永远不会产生变化。 –

+0

这就是我原先认为的要求,但我把它理解为'只有当后面两个值大于x'时才会产生“。但是,如果情况并非如此,那么确保这里的两个slns都需要改变。如果操作系统具有MCVE,那么测试会告诉我们是否正确:-) –

+1

第一个值总是需要产生,因为它随后被用作参考点。以下所有数字将与参考值进行比较。产生的下一个数字应该是距原始参考点“距离> 10”。随后的收益价值将成为下一个数字的新参考点。 – lapsus