简体   繁体   中英

Get the current location after an interval using RX

I'm using reactive extensions in my wp7 app that I'm making and I wish to get the the current location at certain intervals (intervals are based on a user setting). For this example lets say after every 5 seconds I wish to know the current location from the GeoCoordinateWatcher.

I have read some places that I can use .Delay(5 Seconds) but wouldn't that just delay the stream of position changes? As I am only after the current position, would .Delay(5 seconds).Last() work for what I want?

My code so far

if (LocationServices == null)
     LocationServices = new GeoCoordinateWatcher(GeoPositionAccuracy.High)
     {
        MovementThreshold = 2
     };

// Take the first ready status from the GeoCoordinateWatcher
var status = (from o in Observable.FromEvent<GeoPositionStatusChangedEventArgs>                  LocationServices, "StatusChanged")
              where o.EventArgs.Status == GeoPositionStatus.Ready
              select o);

status.Subscribe();

var pos = (from s in status
           from p in Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(LocationServices, "PositionChanged")
           select p.EventArgs.Position); // Do something here to delay?

        pos.Subscribe(LastPos =>
        {
            // Do something with LastPos
        }
        );

        LocationServices.Start();

I'm thinking something like this would work?

var pos = (from s in status
           from p in Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(LocationServices, "PositionChanged")
           select p.EventArgs.Position).Delay( var pos = (from s in status
                   from p in Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(LocationServices, "PositionChanged")
                   select p.EventArgs.Position).TakeLast(1).Delay(new TimeSpan(0,0,5));

        pos.Subscribe(Lastpos =>
        {
           // Do something with Lastpos 
        }
        );;           

Edit: Nope it doesn't work

There are a few different rate limiting operators in RX. Sample is the closest to what you describe, but it will not generate a notification every 5 seconds if the source is not producing new notifications (in other words: Sample = "no more often than").

You should be able to get a polling effect by combining a few other operators. To start, we will need Interval to get the ticks and CombineLatest to do the sampling at the ticks. However, CombineLatest will output a result both on ticks and notifications from the original source. To deal with that, we can use a combination of Scan , Where and Select . In the end you should have something like:

IObservable<T> Poll<T>(this IObservable<T> source, TimeSpan interval)
{
    //error checking goes here
    return source.CombineLatest(Observable.Interval(interval),
                                Tuple.Create)
                 .Scan(Tuple.Create(string.Empty, -1L, -1L),
                       (a, t) => Tuple.Create(t.Item1, t.Item2, a.Item2))
                 .Where(t => t.Item2 != t.Item3)
                 .Select(t => t.Item1);
}

A few notes about the code you posted:

var pos = (from s in status
           from p in Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(LocationServices, "PositionChanged")
           select p.EventArgs.Position);

This makes a new subscription to the PositionChanged event every time the status event is triggered as ready. This will eventually cause position changes to be reported multiple times, which is probably not what you want. You probably want something more like:

var status = Observable.FromEvent<...>(LocationServices, "StatusChanged");
var readys = status.Where(o => o.EventArgs.Status == GeoPositionStatus.Ready);
var notReadys = status.Where(o => o.EventArgs.Status != GeoPositionStatus.Ready);
var positions = Observable.FromEvent<...>)(LocationServices, "PositionChanged");

var readyPositions = from r in readys
                     from p in positions.TakeUntil(notReadys)
                     select p;
//now you can use the Poll operator
readyPositions = readyPositions.Poll(TimeSpan.FromSeconds(5));

EDIT Upon further review, if all you want to do is poll the position every so often, there is no need to handle either event. You can simply check the properties on a timer.

var readyPositions = from tick in Observable.Interval(TimeSpan.FromSeconds(5))
                     where LocationServices.Status == GeoPositionStatus.Ready
                     select LocationServices.Position;

If you only want the timer to run while the watcher is "Ready", you can use the status event, but still don't need to use the position event.

//using variable definitions from above (readys, notReadys)
var readyPositions = from r in readys
                     from i in Observable.Interval(TimeSpan.FromSeconds(5))
                                         .TakeUnitl(notReadys)
                     select LocationServices.Position;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM