//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
using System;
using System.Runtime.InteropServices;
using System.Text;
#if PocketPC
using OpenNETCF.ComponentModel;
using OpenNETCF.Threading;
using ThreadOpenNETCF.Threading.Thread2;
#endif
namespace GMap.NET.GPS{
public delegate void LocationChangedEventHandler(object sender, LocationChangedEventArgs args);
public delegate void DeviceStateChangedEventHandler(object sender, DeviceStateChangedEventArgs args);
/// <summary>
/// Summary description for GPS.
/// </summary>
public class Gps
{
// handle to the gps device
IntPtr gpsHandle = IntPtr.Zero;
// handle to the native event that is signalled when the GPS
// devices gets a new location
IntPtr newLocationHandle = IntPtr.Zero;
// handle to the native event that is signalled when the GPS
// device state changes
IntPtr deviceStateChangedHandle = IntPtr.Zero;
// handle to the native event that we use to stop our event
// thread
IntPtr stopHandle = IntPtr.Zero;
// holds our event thread instance
Thread gpsEventThread = null;
event LocationChangedEventHandler locationChanged;
/// <summary>
/// Event that is raised when the GPS locaction data changes
/// </summary>
public event LocationChangedEventHandler LocationChanged
{
add
{
locationChanged += value;
// create our event thread only if the user decides to listen
CreateGpsEventThread();
}
remove
{
locationChanged -= value;
}
}
event DeviceStateChangedEventHandler deviceStateChanged;
/// <summary>
/// Event that is raised when the GPS device state changes
/// </summary>
public event DeviceStateChangedEventHandler DeviceStateChanged
{
add
{
deviceStateChanged += value;
// create our event thread only if the user decides to listen
CreateGpsEventThread();
}
remove
{
deviceStateChanged -= value;
}
}
/// <summary>
/// True: The GPS device has been opened. False: It has not been opened
/// </summary>
public bool Opened
{
get
{
return gpsHandle != IntPtr.Zero;
}
}
~Gps()
{
// make sure that the GPS was closed.
Close();
}
/// <summary>
/// Opens the GPS device and prepares to receive data from it.
/// </summary>
public void Open()
{
if(!Opened)
{
// create handles for GPS events
newLocationHandle = CreateEvent(IntPtr.Zero, 0, 0, null);
deviceStateChangedHandle = CreateEvent(IntPtr.Zero, 0, 0, null);
stopHandle = CreateEvent(IntPtr.Zero, 0, 0, null);
gpsHandle = GPSOpenDevice(newLocationHandle, deviceStateChangedHandle, null, 0);
// if events were hooked up before the device was opened, we'll need
// to create the gps event thread.
if(locationChanged != null || deviceStateChanged != null)
{
CreateGpsEventThread();
}
}
}
/// <summary>
/// Closes the gps device.
/// </summary>
public void Close()
{
if(gpsHandle != IntPtr.Zero)
{
GPSCloseDevice(gpsHandle);
gpsHandle = IntPtr.Zero;
}
// Set our native stop event so we can exit our event thread.
if(stopHandle != IntPtr.Zero)
{
EventModify(stopHandle, eventSet);
}
// wait exit
if(gpsEventThread != null && gpsEventThread.IsAlive)
{
//gpsEventThread.Join(4444);
}
}
/// <summary>
/// Get the position reported by the GPS receiver
/// </summary>
/// <returns>GpsPosition class with all the position details</returns>
public GpsPosition GetPosition()
{
return GetPosition(TimeSpan.Zero);
}
/// <summary>
/// Get the position reported by the GPS receiver that is no older than
/// the maxAge passed in
/// </summary>
/// <param name="maxAge">Max age of the gps position data that you want back.
/// If there is no data within the required age, null is returned.
/// if maxAge == TimeSpan.Zero, then the age of the data is ignored</param>
/// <returns>GpsPosition class with all the position details</returns>
public GpsPosition GetPosition(TimeSpan maxAge)
{
GpsPosition gpsPosition = null;
if(Opened)
{
// allocate the necessary memory on the native side. We have a class (GpsPosition) that
// has the same memory layout as its native counterpart
IntPtr ptr = Utils.LocalAlloc(Marshal.SizeOf(typeof(GpsPosition)));
// fill in the required fields
gpsPosition = new GpsPosition();
gpsPosition.dwVersion = 1;
gpsPosition.dwSize = Marshal.SizeOf(typeof(GpsPosition));
// Marshal our data to the native pointer we allocated.
Marshal.StructureToPtr(gpsPosition, ptr, false);
// call native method passing in our native buffer
int result = GPSGetPosition(gpsHandle, ptr, 500000, 0);
if(result == 0)
{
// native call succeeded, marshal native data to our managed data
gpsPosition = (GpsPosition) Marshal.PtrToStructure(ptr, typeof(GpsPosition));
if(maxAge != TimeSpan.Zero)
{
// check to see if the data is recent enough.
if(!gpsPosition.Time.HasValue || DateTime.Now - maxAge > gpsPosition.Time)
{
gpsPosition = null;
}
}
}
// free our native memory
Utils.LocalFree(ptr);
}
return gpsPosition;
}
/// <summary>
/// Queries the device state.
/// </summary>
/// <returns>Device state information</returns>
public GpsDeviceState GetDeviceState()
{
GpsDeviceState device = null;
// allocate a buffer on the native side. Since the
IntPtr pGpsDevice = Utils.LocalAlloc(GpsDeviceState.GpsDeviceStructureSize);
// GPS_DEVICE structure has arrays of characters, it's easier to just
// write directly into memory rather than create a managed structure with
// the same layout.
Marshal.WriteInt32(pGpsDevice, 1); // write out GPS version of 1
Marshal.WriteInt32(pGpsDevice, 4, GpsDeviceState.GpsDeviceStructureSize); // write out dwSize of structure
int result = GPSGetDeviceState(pGpsDevice);
if(result == 0)
{
// instantiate the GpsDeviceState class passing in the native pointer
device = new GpsDeviceState(pGpsDevice);
}
// free our native memory
Utils.LocalFree(pGpsDevice);
return device;
}
/// <summary>
/// Creates our event thread that will receive native events
/// </summary>
private void CreateGpsEventThread()
{
// we only want to create the thread if we don't have one created already
// and we have opened the gps device
if(gpsEventThread == null && gpsHandle != IntPtr.Zero)
{
// Create and start thread to listen for GPS events
gpsEventThread = new Thread(new System.Threading.ThreadStart(WaitForGpsEvents));
gpsEventThread.IsBackground = false;
gpsEventThread.Name = "GMap.NET GpsEvents";
gpsEventThread.Start();
}
}
/// <summary>
/// Method used to listen for native events from the GPS.
/// </summary>
private void WaitForGpsEvents()
{
//lock (this)
{
bool listening = true;
// allocate 3 handles worth of memory to pass to WaitForMultipleObjects
IntPtr handles = Utils.LocalAlloc(12);
// write the three handles we are listening for.
Marshal.WriteInt32(handles, 0, stopHandle.ToInt32());
Marshal.WriteInt32(handles, 4, deviceStateChangedHandle.ToInt32());
Marshal.WriteInt32(handles, 8, newLocationHandle.ToInt32());
while(listening)
{
int obj = WaitForMultipleObjects(3, handles, 0, -1);
if(obj != waitFailed)
{
switch(obj)
{
case 0:
// we've been signalled to stop
listening = false;
break;
case 1:
// device state has changed
if(deviceStateChanged != null)
{
deviceStateChanged(this, new DeviceStateChangedEventArgs(GetDeviceState()));
}
break;
case 2:
// location has changed
if(locationChanged != null)
{
locationChanged(this, new LocationChangedEventArgs(GetPosition()));
}
break;
}
}
}
// free the memory we allocated for the native handles
Utils.LocalFree(handles);
if(newLocationHandle != IntPtr.Zero)
{
CloseHandle(newLocationHandle);
newLocationHandle = IntPtr.Zero;
}
if(deviceStateChangedHandle != IntPtr.Zero)
{
CloseHandle(deviceStateChangedHandle);
deviceStateChangedHandle = IntPtr.Zero;
}
if(stopHandle != IntPtr.Zero)
{
CloseHandle(stopHandle);
stopHandle = IntPtr.Zero;
}
// clear our gpsEventThread so that we can recreate this thread again
// if the events are hooked up again.
gpsEventThread = null;
}
}
public double GetDistance(double p1Lat, double p1Lng, double p2Lat, double p2Lng)
{
double dLat1InRad = p1Lat * (Math.PI / 180);
double dLong1InRad = p1Lng * (Math.PI / 180);
double dLat2InRad = p2Lat * (Math.PI / 180);
double dLong2InRad = p2Lng * (Math.PI / 180);
double dLongitude = dLong2InRad - dLong1InRad;
double dLatitude = dLat2InRad - dLat1InRad;
double a = Math.Pow(Math.Sin(dLatitude / 2), 2) + Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) * Math.Pow(Math.Sin(dLongitude / 2), 2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
double dDistance = EarthRadiusKm * c;
return dDistance;
}
/// <summary>
/// Radius of the Earth
/// </summary>
public double EarthRadiusKm = 6378.137; // WGS-84
#region PInvokes to gpsapi.dll
[DllImport("gpsapi.dll")]
static extern IntPtr GPSOpenDevice(IntPtr hNewLocationData, IntPtr hDeviceStateChange, string szDeviceName, int dwFlags);
[DllImport("gpsapi.dll")]
static extern int GPSCloseDevice(IntPtr hGPSDevice);
[DllImport("gpsapi.dll")]
static extern int GPSGetPosition(IntPtr hGPSDevice, IntPtr pGPSPosition, int dwMaximumAge, int dwFlags);
[DllImport("gpsapi.dll")]
static extern int GPSGetDeviceState(IntPtr pGPSDevice);
#endregion
#region PInvokes to coredll.dll
[DllImport("coredll.dll")]
static extern IntPtr CreateEvent(IntPtr lpEventAttributes, int bManualReset, int bInitialState, StringBuilder lpName);
[DllImport("coredll.dll")]
static extern int CloseHandle(IntPtr hObject);
const int waitFailed = -1;
[DllImport("coredll.dll")]
static extern int WaitForMultipleObjects(int nCount, IntPtr lpHandles, int fWaitAll, int dwMilliseconds);
const int eventSet = 3;
[DllImport("coredll.dll")]
static extern int EventModify(IntPtr hHandle, int dwFunc);
#endregion
}
}
|