Click here to Skip to main content
15,892,674 members
Articles / Mobile Apps / Windows Phone 7

Bike In City with Windows Phone 7

Rate me:
Please Sign up or sign in to vote.
4.97/5 (62 votes)
5 Jan 2011CPOL33 min read 111.1K   3.1K   101  
Learn how to build a small mobile application to visualize data from the city bike sharing system. Get the nearest stations, find the number of free bikes, and compute directions to other stations.
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Device.Location;
using System.Threading;
using Microsoft.Phone.Controls.Maps;

namespace BikeInCity.Utils
{
  public class GeoCoordinateSimulator : IGeoPositionWatcher<GeoCoordinate>
  {
    //represents left down corner of city bordering rectangle
    private GeoCoordinate _leftCorner;

    //represents right up corner of city bordering rectangle
    private GeoCoordinate _rightCorner;

    //direction in which the current position will change
    private double _dLat;
    private double _dLong;

    //Time interval between 2 changes of position
    private int _interval;

    private GeoPosition<GeoCoordinate> _position;

    //timer to fire position changes
    private Timer _timer;
    public Object _timerState;

    /// <summary>
    /// Constructs a simulator which changes current position. Simulates user moving in one directions
    /// until he hits the borders specified by the parameters.
    /// </summary>
    /// <param name="left">Lower left corner of the city</param>
    /// <param name="right">Upper right corner of the city</param>
    /// <param name="interval"></param>
    public GeoCoordinateSimulator(GeoCoordinate left, GeoCoordinate right, int interval)
    {
      _leftCorner = left;
      _rightCorner = right;

      //the values should be in between -180 and 180
      if (_leftCorner.Longitude > 180)
      {
        _leftCorner.Longitude -= 360;
      }

      if (_rightCorner.Longitude > 180)
      {
        _leftCorner.Longitude -= 360;
      }

      //check if the city border parameters are correct
      if(_leftCorner.Latitude > _rightCorner.Latitude ||
        _leftCorner.Longitude > _rightCorner.Longitude){
          throw new ArgumentException("Left corner values should be smaller than right corner values");
        }

      double latRange = _rightCorner.Latitude - _leftCorner.Latitude;
      double longRange = _rightCorner.Longitude - _leftCorner.Longitude;

      //setting current position to the midle of the city
      _position = new GeoPosition<GeoCoordinate>(DateTime.Now,
        new GeoCoordinate(_leftCorner.Latitude + latRange / 2, _leftCorner.Longitude + longRange / 2));

      //set the interval at which the timer should fire
      _interval = interval;
    }

    #region IGeoPositionWatcher<GeoCoordinate> Members

    public GeoPosition<GeoCoordinate> Position
    {
      get { return _position; }
    }

    public event EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>> PositionChanged;

    
    /// <summary>
    /// According to MSDN:
    /// Start acquiring location data, specifying whether or not to suppress prompting for permissions. This method returns synchronously.
    /// So in case of simulator I can just call the parameterless constructor. This method is not used, but has to be here to implement the interface correctlly.
    /// </summary>
    /// <param name="suppressPermissionPrompt"></param>
    public void Start(bool suppressPermissionPrompt)
    {
      Start();
    }

    /// <summary>
    /// Method to start the simulation - start the timer
    /// </summary>
    public void Start()
    {
      _timer = new Timer(new TimerCallback(TimerCallBack), _timerState, 0, _interval);
    }

    
    public GeoPositionStatus Status
    {
      get {
        if (_timer != null)
        {
          return GeoPositionStatus.Ready;
        }
        return GeoPositionStatus.Disabled;
      }
    }

    public event EventHandler<GeoPositionStatusChangedEventArgs> StatusChanged;

    public void Stop()
    {
      _timer.Dispose();
    }

    /// <summary>
    /// This method is not used in the simulation
    /// </summary>
    /// <param name="suppressPermissionPrompt"></param>
    /// <param name="timeout"></param>
    /// <returns></returns>
    public bool TryStart(bool suppressPermissionPrompt, TimeSpan timeout)
    {
      Start();
      return true;
    }

    #endregion

    /// <summary>
    /// Callback of the timer. This method will set the new position. In most cases it will add the _dLat and _dLong values
    /// to the current position - to simulate the move in the same directions. When it will hit the city borders, it will generate 
    /// new values randomly for the direction. Also the case of where the difference in direction would be zero is not allowed.
    /// </summary>
    /// <param name="obj"></param>
    public void TimerCallBack(Object obj)
    {
      Random r = new Random();
      double newLatitude, newLongitude;

      while (!IsInRange(newLatitude = this.Position.Location.Latitude + _dLat,
      newLongitude = this.Position.Location.Longitude + _dLong) || (_dLat==0.0 && _dLong==0.0))
      {
        _dLat = (r.NextDouble() - 0.5) * BikeConst.GPS_SIMULATOR_STEP;
        _dLong = (r.NextDouble() - 0.5) * BikeConst.GPS_SIMULATOR_STEP;
      }

      //set new position
      _position = new GeoPosition<GeoCoordinate>(DateTime.Now, new GeoCoordinate(newLatitude,newLongitude));

      //fire the event if there are any subscribers
      if (this.PositionChanged != null)
      {
        PositionChanged(this, new GeoPositionChangedEventArgs<GeoCoordinate>(this.Position));
      }
    }
   
    /// <summary>
    /// Returns true if the specified position is still in the borders of the city
    /// </summary>
    /// <param name="lat"></param>
    /// <param name="lng"></param>
    /// <returns></returns>
    public bool IsInRange(double lat,double lng){
      return (lat > _leftCorner.Latitude && lng > _leftCorner.Longitude
        && lat < _rightCorner.Latitude && lng < _rightCorner.Longitude) ;
    }
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Junior) OCTO Technology
Czech Republic Czech Republic
Writing software at ITG RFQ-hub.
LinkedIn
Blog
GitHub
Articles at OCTO blog

Comments and Discussions