When using GPX in Xcode to simulate location changes, is there a way to control the speed?
Asked Answered
C

6

29

I'm using the following GPX file in Xcode 4.2 to simulate a location change. It works well, but I can't control the speed of the location change. stamp seems to be not working. Does anyone have a solution for this?

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode"> 
    <wpt lat="37.331705" lon="-122.030237"></wpt>
    <wpt lat="37.331705" lon="-122.030337"></wpt>
    <wpt lat="37.331705" lon="-122.030437"></wpt>
    <wpt lat="37.331705" lon="-122.030537"></wpt>
</gpx>
Cladoceran answered 24/2, 2012 at 23:26 Comment(0)
A
13

I don't think (know) that this is possible in the GPX directly, but you can test location change with Instruments/Automation.

You'd use a script like:

var target = UIATarget.localTarget();
target.setLocation(<location);
target.delay(5);
target.setLocation(...);

And so on. I took this example from the WWDC11 video (Testing your location-aware applications)

I'm aware that this doesn't actually let you define the speed, but the delays somehow account for that I hope. Maybe that'll help you.

Anadromous answered 24/2, 2012 at 23:35 Comment(2)
Thanks JiaYow, that's exactly what I'm looking for! I tried it, works perfectly. Using Instruments seem to make me losing NSLog in console though.Cladoceran
UI automation part starts at 31:10 in the video.Jaquiss
C
30

Xcode support simulate speed change with a GPX file.

Provide one or more waypoints containing a latitude/longitude pair. If you provide one waypoint, Xcode will simulate that specific location. If you provide multiple waypoints, Xcode will simulate a route visitng each waypoint.

Optionally provide a time element for each waypoint. Xcode will interpolate movement at a rate of speed based on the time elapsed between each waypoint. If you do not provide a time element, then Xcode will use a fixed rate of speed. Waypoints must be sorted by time in ascending order.

Write like this:

<wpt lat="39.96104510" lon="116.4450860">
    <time>2010-01-01T00:00:00Z</time>
</wpt>
<wpt lat="39.96090940" lon="116.4451400">
    <time>2010-01-01T00:00:05Z</time>
</wpt>
...
<wpt lat="39.96087240" lon="116.4450430">
    <time>2010-01-01T00:00:09Z</time>
</wpt>

About -1 speed

The CoreLocation object’s speed will always be -1 during simulation. A possible workaround is save a last location then calculate the speed ourselves. Sample code:

CLLocationSpeed speed = location.speed;
if (speed < 0) {
    // A negative value indicates an invalid speed. Try calculate manually.
    CLLocation *lastLocation = self.lastLocation;
    NSTimeInterval time = [location.timestamp timeIntervalSinceDate:lastLocation.timestamp];

    if (time <= 0) {
        // If there are several location manager work at the same time, an outdated cache location may returns and should be ignored.
        return;
    }

    CLLocationDistance distanceFromLast = [lastLocation distanceFromLocation:location];
    if (distanceFromLast < DISTANCE_THRESHOLD
        || time < DURATION_THRESHOLD) {
        // Optional, dont calculate if two location are too close. This may avoid gets unreasonable value.
        return;
    }
    speed = distanceFromLast/time;
    self.lastLocation = location;
}
Chamois answered 5/4, 2016 at 3:24 Comment(7)
For some reason the site with the gpx standard (www.topografix.com/gpx_manual.asp) provides an example like this: <time>2002-02-10T21:01:29.250Z</time>. Note the fractions of a second. Had to remove the fraction to get it to load in XCode.Alleneallentown
@Alleneallentown you can replace these number using regex "\.\d{3}Z" => "Z"Chamois
When I try this Xcode will read the file, but still indicates speed is "-1.00 mps". Also got an error from Xcode if all time values were the same, it wants them incrementing (though then nothing seems to happen with them...Balderdash
@KendallHelmstetterGelner the time elapsed must be unequal each waypoint.Chamois
The -1 mps was after I changed all of the time values to be different, I had them advancing by a second each time but the CoreLocation object returned to me still claimed the speed was -1 mps. This is in Xcode8beta6.Balderdash
Had the same issue in Xcode7 (-1 mps) while simulating track with gpx ( no idea how to solve thisInflection
@KendallHelmstetterGelner Answer updated about -1 speed.Chamois
H
16

I don't think you can do it with GPX files. But it is easy with Automation tool within Intruments. Here is one of the scripts I use myself for my app testing and screenshots gathering:

var target = UIATarget.localTarget();

// speed is in meters/sec
var points = [
          {location:{latitude:48.8899,longitude:14.2}, options:{speed:8, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:48.8899,longitude:14.9}, options:{speed:11, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:48.8899,longitude:14.6}, options:{speed:12, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:48.8899,longitude:14.7}, options:{speed:13, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:49.2,longitude:14.10}, options:{speed:15, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:49.4,longitude:14.8}, options:{speed:15, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:48.8899,longitude:14.9}, options:{speed:9, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:48.8899,longitude:15.1}, options:{speed:8, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          {location:{latitude:48.8899,longitude:16.1}, options:{speed:3, altitude:200, horizontalAccuracy:10, verticalAccuracy:15}},
          ];

for (var i = 0; i < points.length; i++)
{
target.setLocationWithOptions(points[i].location,points[i].options);
target.captureScreenWithName(i+"_.png");
target.delay(1.0);
}

I created step by step walkthrough for how I used location simulation with Automation and Leaks to grab screenshots and find leaks

Headpiece answered 29/7, 2012 at 12:32 Comment(1)
Do you know how we can change location while the app is running in the background? I.e. this SO question here: #22345358Argeliaargent
A
13

I don't think (know) that this is possible in the GPX directly, but you can test location change with Instruments/Automation.

You'd use a script like:

var target = UIATarget.localTarget();
target.setLocation(<location);
target.delay(5);
target.setLocation(...);

And so on. I took this example from the WWDC11 video (Testing your location-aware applications)

I'm aware that this doesn't actually let you define the speed, but the delays somehow account for that I hope. Maybe that'll help you.

Anadromous answered 24/2, 2012 at 23:35 Comment(2)
Thanks JiaYow, that's exactly what I'm looking for! I tried it, works perfectly. Using Instruments seem to make me losing NSLog in console though.Cladoceran
UI automation part starts at 31:10 in the video.Jaquiss
L
9

If you don't want to deal with automator, you can get it working with just a GPX file. The trick is to create a bunch of points.

For example, instead of creating just 2 points to go from A to B, create a bunch of intermediary points between them. This works because the location simulator takes constant time to travel from one point to another, no matter the distance between the two points.

Rather than having to create a bunch of points manually, you can use the following code.

Instructions:

  1. Paste the code below, tweaking the kDesiredSpeed constant to your liking.
  2. Add a UITapGestureRecognizer to your map view, linking it up to mapViewTapped:
  3. Add buttons that call startRecordingPoints and stopRecordingPoints.
  4. Run the app.
  5. Tap the startRecordingPoints button.
  6. Tap where the route should start.
  7. Tap another location in the map. This will generate X amount of points between the last node and the new node so that it will appear to move at the speed you want.
  8. Repeat the previous step as many times as you want.
  9. Press stop recording.
  10. Copy the console output.
  11. File > New File...
  12. Choose Resource > GPX File
  13. Paste the contents and save the file.
  14. Tap the location arrow in the debugger and choose your GPX file.
  15. Sit back and watch as the location is updated at exactly the speed you want!

Code:

@property (strong, nonatomic) CLLocation *lastRecordedPoint;
@property (strong, nonatomic) NSMutableString *recordingOutput;

...

- (IBAction)mapViewTapped:(UITapGestureRecognizer *)sender {
    if (sender.state != UIGestureRecognizerStateEnded || !self.recordingOutput) {
        return;
    }

    CLLocationCoordinate2D coord = [self.mapView convertPoint:[sender locationInView:self.mapView]
                                         toCoordinateFromView:self.mapView];
    [self recordPoint:coord];
}

- (void)recordPoint:(CLLocationCoordinate2D)newPoint {
    const CGFloat kAppleTravelTime = 2; // the default time it takes to travel from one point to another
    const CGFloat kDesiredSpeed = 6; // meters per sec
    const CGFloat kDesiredDistanceBetweenPoints = kDesiredSpeed * kAppleTravelTime;
    NSString * const kFormatString = @"    <wpt lat=\"%f\" lon=\"%f\"></wpt>\n";

    CLLocation *newLocation = [[CLLocation alloc] initWithLatitude:newPoint.latitude longitude:newPoint.longitude];
    NSInteger numberOfPoints = 1;
    if (self.lastRecordedPoint) {
        CLLocationDistance distance = [self.lastRecordedPoint distanceFromLocation:newLocation];
        numberOfPoints = MAX(round(distance / kDesiredDistanceBetweenPoints), 1);
        CGFloat deltaLatitude = newPoint.latitude - self.lastRecordedPoint.coordinate.latitude;
        CGFloat deltaLongitude = newPoint.longitude - self.lastRecordedPoint.coordinate.longitude;
        for (NSInteger i = 0; i < numberOfPoints; i++) {
            CLLocationDegrees latitude = self.lastRecordedPoint.coordinate.latitude + (numberOfPoints/distance * deltaLatitude) * (i+1);
            CLLocationDegrees longitude = self.lastRecordedPoint.coordinate.longitude + (numberOfPoints/distance * deltaLongitude) * (i+1);
            [self.recordingOutput appendFormat:kFormatString, latitude, longitude];
        }
    } else {
        [self.recordingOutput appendFormat:kFormatString, newPoint.latitude, newPoint.longitude];
    }
    NSLog(@"Recorded %ld point(s) to: %f,%f", (long)numberOfPoints, newPoint.latitude, newPoint.longitude);

    self.lastRecordedPoint = newLocation;
}


- (void)startRecordingPoints {
    NSLog(@"Started recording points. Tap anywhere on the map to begin recording points.");
    self.recordingOutput = [NSMutableString string];
    [self.recordingOutput appendString:@"<?xml version=\"1.0\"?>\n<gpx version=\"1.1\" creator=\"Xcode\">\n"];
    self.lastRecordedPoint = nil;
}

- (void)stopRecordingPoints {
    [self.recordingOutput appendString:@"</gpx>"];
    NSLog(@"Done recording, here is your gpx file: \n%@", self.recordingOutput);
    self.recordingOutput = nil;
}

Disclaimer: kAppleTravelTime = 2 is just a guess. If you have a more accurate value, please post it in a comment.

Laboured answered 17/4, 2014 at 8:18 Comment(0)
F
6

Update : Oct, 2018 (Swift 4, Xcode 9.4)

I have found a very simple solution. although it does not allow to specify exact speed, however speed can be controller by specifying the distance between gps points. The speed is directly proportional to distance between two gps points.

  1. Generate GPX points from gpxGenerator.com
  2. In Xcode, Create a new GPX from file -> New File ( Or Command N). Search for gpx
  3. Paste the GPX Points that you got from gpxGenerator.com in that gpx file
  4. Delete all the time tags (that's the meat of it. Don't Ignore this step)
  5. Run you app and select your gpx file from debug menu. (see screen shot) In my case, its name is SanFranciscoToNewYork

That's all. Now, the simulator or real device should simulate the points in your gpx file with sp

Select your gpx file from menu


Demo

enter image description here

As you can see in the demo, the marker is moving very fast. It's not the default slow speed. I

Note: This is my sample GPX File that i used for the demo

Finale answered 11/10, 2018 at 14:44 Comment(2)
This return speed of -1 tooKephart
This regex can be used to find and replace all timestamps <time>[0-9-T:Z]+</time>Bigley
S
2

There is also a method which lets you pass in the speed and some other properties:

target.setLocationWithOptions({latitude: 46.546928, longitude: 11.867127}, {altitude: 200.0, speed: 5});

(Check this AppleDoc for more details)

You can still see your NSLog's in the console application (/Applications/Utilities/Console.app). Just add a filter to get proper results.

Savage answered 30/3, 2012 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.