c#,winform实现获取当前经纬度坐标(极其便捷)

来源:互联网 发布:win10有线网络设置 编辑:程序博客网 时间:2024/05/21 01:43

        近期开发winform桌面应用时,需要获取当前的经纬度坐标,并显示在地图上(我们要实现的是arcgis engine的地图,但事实上不论是百度还是googel这些地图,只要获取了当前的经纬度坐标,一切都好说)。我花费了快一天的时间终于解决这个问题。急切地想解决问题的话,请跳到Part 2直接扫一眼,copy代码用起,否则可以慢慢读,有帮助。

Part one

        在国内的网站找了一遍又一遍,获取当前设备所在的经纬度坐标(PC)主要有两种方式:

        ①调用第三方定位api;http://51.ruyo.net/p/2759.html

        ②调用HTML 5方法;http://www.w3school.com.cn/html5/html_5_geolocation.asp

        然而这两种方法都不好用,第一种方法,我首先考虑的是百度地图,发现百度的高精度ip定位api已经关闭,只有普通的IP定位api(精确到城市就不错了),好像高德也有,但我没有仔细研究了。如果能找到api的话,可是可以用的,但一般要注册较为麻烦。

       第二种方法,有非常多的人用,基本是web应用中调用的(asp.net之流)。HTML 5这个Geolocation方法确实好用,我自己也写了对应的页面,效果非常好。能够正常运行。

<!DOCTYPE html><html>    <head>        <meta charset="UTF-8">        <title>H5地理位置Demo</title>        <style type="text/css">        body, html{width: 100%;height: 100%;margin:0;font-family:"微软雅黑";background-color: white;text-align: center;}        #demo{width:100%;height:20%;}    </style>    <body>        <div id="demo">test</div>        <input type="button" value="定位" onClick="getLocation()" >    </body>    <script type="text/javascript">       var x=document.getElementById("demo");function getLocation()  {  if (navigator.geolocation)    {    var position=navigator.geolocation.getCurrentPosition(showPosition);    //showPosition(position);    }  else{x.innerHTML="Geolocation is not supported by this browser.";}  }function showPosition(position)  {  x.innerHTML="Latitude: " + position.coords.latitude +  "<br />Longitude: " + position.coords.longitude;  }    </script></html>

     上述html的运行效果如下图所示:

       

     讲道理,之后我天真的以为,我只需要在文本winform里面添加一个webBrowser控件,然后进行c#和html,js的交互就可以了。但是webBrowser打开后,什么事情都没有发生。一看HTML 5关于geolocation的描述,我有点想哭的感觉

定位用户的位置HTML5 Geolocation API 用于获得用户的地理位置。鉴于该特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的。

       ……在浏览器中直接调用上面的html时,会弹出一个窗口,是否允许调用位置坐标信息,但是在webBrowser中运行的时候,并不会出现这种情况。我用中文在百度,bing,goole中搜了很长时间,但是基本所有的回答或解决方案都没什么营养。唯一有点价值的就是说是浏览器的问题,从这个思路出发,我考虑更换webBrowser的浏览器,但网上的各种博客真的让我看的很头疼。无奈之下,我只好在google上开始用英文搜,搜了一会,诶,大神就蹦出来了。stackoverflow上的某大神针对和我碰到的类似的问题做出了回复:

       大神核心意思就是:“由于 sercurity bar 在webBrowser中无法被调用,因此HTML的geolocation无法正常工作。我一会就写了一个自定义的浏览器,把我写的浏览器内置在winform程序中,做一些设置就可以正常运行了。”大神回答的链接是:http://stackoverflow.com/questions/14435585/c-sharp-desktop-application-doesnt-share-my-physical-location,我把大神的解决方案放一份在这里:

The Context

When JavaScript tries to access the location object in IE 10 you are presented with the security bar asking for you to allow the access to your location. The difference for a file which is on the local drive or a network share is that you are not presented with the option to always allow access, but only once (Allow once).

For whatever reason, this security bar doesn't show up in the WebBrowser control (even if I've tried setting the Information Bar Handling for the aplication's .exe, but it seems not to have any effect).

This is why every time when the script executes nothing happens in the web browser control. It is actually blocked by the information bar.

The Solution

What needs to be done:

  1. Emulate a web server inside the application. I've used a Simple C# Web Server class to serve the content. This way, even if there is no web server on the local machine, we may intercept requests to a specific URL address and port and serve the content we want to.

  2. Add the test1.html document to the project and use it's content in the server response. Just add the file into your project, next to the "Program.cs" file and set it's Copy to Output Directory property value to Copy always.

How It Works

First, we need to instantiate a web browser control. Then, navigate to the test1.html file. When the document is loaded, we first check if the web server is not instantiated. If this, we create an instance of it and then we read and store the web browser's HTMl source in the response variable, which we pass to the WebServer constructor.

The http://localhost:9999 registers the HttpListener to that prefix, so every request to this address will be served by our simple web server.

Next, we navigate to that address. When the web server will receive the request, it will deliver the content of the _staticContent variable, which had it's value assigned in the web server's constructor.

After the server delivers the document to the web browser, the webBrowser1_DocumentCompletedhandler is triggered. But this time, we already have the web server's instance, so execution goes through the else branch. The important thing to notice is that we will asynchronously wait for the JavaScript to execute, get the location and save it to the hidden input elements in the HTML.

One important remark: the first time you launch the application you will not get any location. You first have to leave the application open, so that you have the custom HTTP listener available, and then, perform the steps I described in my other answer, the browsing location beinghttp://localhost:9999. Once you do that, close and reopen the application.

That's it. Everytime you run the application, you will get the location coordinates in a message box.

The Form1 class file (Form1.cs):

public partial class Form1 : Form{    WebServer _ws;    WebBrowser _webBrowser1;    public Form1()    {        InitializeComponent();        _webBrowser1 = new WebBrowser();        _webBrowser1.Visible = false;        var location = Assembly.GetExecutingAssembly().Location;        _webBrowser1.Navigate(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\test1.html");        _webBrowser1.DocumentCompleted += webBrowser1_DocumentCompleted;    }    private void Form1_Load(object sender, EventArgs e)    {    }    async void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)    {        if (_ws == null)        {            var html = _webBrowser1.Document.GetElementsByTagName("html");            var response = html[0].OuterHtml;            _ws = new WebServer(response, "http://localhost:9999/");            _ws.Run();            _webBrowser1.Navigate("http://localhost:9999/");        }        else        {            string latitude = "";            string longitude = "";            await Task.Factory.StartNew(() =>            {                while (string.IsNullOrEmpty(latitude))                {                    System.Threading.Thread.Sleep(1000);                    if (this.InvokeRequired)                    {                        this.Invoke((MethodInvoker)delegate                        {                            var latitudeEl = _webBrowser1.Document.GetElementById("latitude");                            var longitudeEl = _webBrowser1.Document.GetElementById("longitude");                            latitude = latitudeEl.GetAttribute("value");                            longitude = longitudeEl.GetAttribute("value");                        });                    }                }            });            MessageBox.Show(String.Format("Latitude: {0} Longitude: {1}", latitude, longitude));        }    }    // credits for this class go to David    // http://www.codehosting.net/blog/BlogEngine/post/Simple-C-Web-Server.aspx    public class WebServer    {        private readonly HttpListener _listener = new HttpListener();        static string _staticContent;        public WebServer(string[] prefixes, string content)        {            _staticContent = content;            foreach (string s in prefixes)                _listener.Prefixes.Add(s);            _listener.Start();        }        public WebServer(string content, params string[] prefixes)            : this(prefixes,  content) { }        public void Run()        {            ThreadPool.QueueUserWorkItem((o) =>            {                try                {                    while (_listener.IsListening)                    {                        ThreadPool.QueueUserWorkItem((c) =>                        {                            var ctx = c as HttpListenerContext;                            try                            {                                byte[] buf = Encoding.UTF8.GetBytes(_staticContent);                                ctx.Response.ContentLength64 = buf.Length;                                ctx.Response.OutputStream.Write(buf, 0, buf.Length);                            }                            catch { } // suppress any exceptions                            finally                            {                                // always close the stream                                ctx.Response.OutputStream.Close();                            }                        }, _listener.GetContext());                    }                }                catch { } // suppress any exceptions            });        }        public void Stop()        {            _listener.Stop();            _listener.Close();        }    }}

The HTML source (test1.html)

<html xmlns="http://www.w3.org/1999/xhtml"><head runat="server">    <title></title>    <meta http-equiv="X-UA-Compatible" content="IE=10" />    <script type="text/javascript">        window.onload = function () {            var latitude = document.getElementById("latitude");            var longitude = document.getElementById("longitude");            function getLocation() {                if (navigator.geolocation) {                    navigator.geolocation.getCurrentPosition(showPosition);                }                else { }            }            function showPosition(position) {                latitude.value = position.coords.latitude;                longitude.value = position.coords.longitude;            }            getLocation();        }    </script></head><body>    <input type="hidden" id="latitude" />    <input type="hidden" id="longitude" /></body></html>

       根据我的测试,大神的方案确实可以使用,但我觉得还是太复杂了(而且大神的程序第一遍执行的时候不能正常运行,第二次开始才可以)……我就是要个经纬度而已,为什么要搞这么复杂……。不死心的我继续搜啊搜,于是曙光就来了,http://csharphelper.com/blog/2016/05/use-the-location-api-to-find-the-computers-latitude-and-longitude-in-c/ 通过using system.Device.Location; 并调用GeoCoordinateWatcher类,可以轻易实现获取经纬度坐标。接下来在Part 2中详细说明。

PART TWO

      首先上两个连接:①http://csharphelper.com/blog/2016/05/use-the-location-api-to-find-the-computers-latitude-and-longitude-in-c/ GeoCoordinateWatcher的应用实例;②https://msdn.microsoft.com/zh-cn/library/system.device.location.geocoordinatewatcher(v=vs.110).aspx GeoCoordinateWatcher的详细说明。

      根据我的测试,无法通过 watcher.try 或 watcher.traystart来直接获得坐标信息,只有通过PositionChanged事件异步地获取坐标信息才能成功。总之,这里放上能用的代码:

using System;using System.Device.Location;namespace GetLocationEvent{    class Program    {        static void Main(string[] args)        {            CLocation myLocation = new CLocation();            myLocation.GetLocationEvent();            Console.WriteLine("Enter any key to quit.");            Console.ReadLine();                    }        class CLocation        {            GeoCoordinateWatcher watcher;            public void GetLocationEvent()            {                this.watcher = new GeoCoordinateWatcher();                this.watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);                bool started = this.watcher.TryStart(false, TimeSpan.FromMilliseconds(2000));                if (!started)                {                    Console.WriteLine("GeoCoordinateWatcher timed out on start.");                }            }            void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)            {                PrintPosition(e.Position.Location.Latitude, e.Position.Location.Longitude);            }            void PrintPosition(double Latitude, double Longitude)            {                Console.WriteLine("Latitude: {0}, Longitude {1}", Latitude, Longitude);            }        }    }}

      为了方便使用,可以把上述代码封装成一个CurrentPosition类,设置一个方法: Static public bool getCurrentPosition(double &lat, double &long); 实现效果如下图所示,精度还是很不错的,和我的真实位置偏差几十米。如果设备上有GPS设备,这个定位就会变的非常精确。




2 0
原创粉丝点击