2016-09-06 36 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

相關問題