diff --git a/GMap.NET.WindowsMobile/GMap.NET.GPS/GPS.cs b/GMap.NET.WindowsMobile/GMap.NET.GPS/GPS.cs new file mode 100644 --- /dev/null +++ b/GMap.NET.WindowsMobile/GMap.NET.GPS/GPS.cs @@ -0,0 +1,390 @@ +// +// 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 Thread=OpenNETCF.Threading.Thread2; +using System.Diagnostics; +#endif + +namespace GMap.NET.GPS +{ + public delegate void LocationChangedEventHandler(object sender, GpsPosition args); + public delegate void DeviceStateChangedEventHandler(object sender, GpsDeviceState args); + + /// + /// Summary description for GPS. + /// + 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; + + /// + /// Event that is raised when the GPS locaction data changes + /// + 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; + + /// + /// Event that is raised when the GPS device state changes + /// + public event DeviceStateChangedEventHandler DeviceStateChanged + { + add + { + deviceStateChanged += value; + + // create our event thread only if the user decides to listen + CreateGpsEventThread(); + } + remove + { + deviceStateChanged -= value; + } + } + + /// + /// True: The GPS device has been opened. False: It has not been opened + /// + public bool Opened + { + get + { + return gpsHandle != IntPtr.Zero; + } + } + + ~Gps() + { + // make sure that the GPS was closed. + Close(); + } + + /// + /// Opens the GPS device and prepares to receive data from it. + /// + 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(); + } + } + } + + /// + /// Closes the gps device. + /// + 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); + } + } + + /// + /// Get the position reported by the GPS receiver + /// + /// GpsPosition class with all the position details + public GpsPosition GetPosition() + { + return GetPosition(TimeSpan.Zero); + } + + /// + /// Get the position reported by the GPS receiver that is no older than + /// the maxAge passed in + /// + /// 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 + /// GpsPosition class with all the position details + 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.UtcNow - maxAge > gpsPosition.Time) + { + gpsPosition = null; + } + } + } + + // free our native memory + Utils.LocalFree(ptr); + } + + return gpsPosition; + } + + /// + /// Queries the device state. + /// + /// Device state information + 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; + } + + /// + /// Creates our event thread that will receive native events + /// + 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(); + } + } + + /// + /// Method used to listen for native events from the GPS. + /// + 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, GetDeviceState()); + } + break; + + case 2: + // location has changed + if(locationChanged != null) + { + locationChanged(this, 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; + + Debug.WriteLine("gps device stopped..."); + } + } + + 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; + } + + /// + /// Radius of the Earth + /// + 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 + } +}