{"action":"opened","issue":{"url":"https://api.github.com/repos/heweixiao/blog/issues/3","repository_url":"https://api.github.com/repos/heweixiao/blog","labels_url":"https://api.github.com/repos/heweixiao/blog/issues/3/labels{/name}","comments_url":"https://api.github.com/repos/heweixiao/blog/issues/3/comments","events_url":"https://api.github.com/repos/heweixiao/blog/issues/3/events","html_url":"https://github.com/heweixiao/blog/issues/3","id":310212735,"number":3,"title":"使用HTML5地理位置定位到城市的方法及注意事项","user":{"login":"heweixiao","id":11675081,"avatar_url":"https://avatars0.githubusercontent.com/u/11675081?v=4","gravatar_id":"","url":"https://api.github.com/users/heweixiao","html_url":"https://github.com/heweixiao","followers_url":"https://api.github.com/users/heweixiao/followers","following_url":"https://api.github.com/users/heweixiao/following{/other_user}","gists_url":"https://api.github.com/users/heweixiao/gists{/gist_id}","starred_url":"https://api.github.com/users/heweixiao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heweixiao/subscriptions","organizations_url":"https://api.github.com/users/heweixiao/orgs","repos_url":"https://api.github.com/users/heweixiao/repos","events_url":"https://api.github.com/users/heweixiao/events{/privacy}","received_events_url":"https://api.github.com/users/heweixiao/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-03-31T04:52:45Z","updated_at":"2018-03-31T04:52:45Z","closed_at":null,"author_association":"OWNER","body":"## 介绍\r\n本文将简述一下如何通过HTML5和百度地图开放平台提供的API来实现对浏览器的定位，同时记录了遇到的问题和解决方案。实现效果为显示出用户所在的省市，即： XXX省 XXX市。\r\n\r\n## 实现思路\r\n\r\n利用HTML5 提供的API获取到用户的经纬度信息，再将用户的经纬度信息传到百度地图开放平台，百度地图开放平台根据提供的坐标信息返回当前的省市。\r\n\r\n## 兼容性及依赖\r\n兼容性：Internet Explorer 9+, Firefox, Chrome, Safari 和 Opera 都支持Geolocation（地理定位）.\r\n\r\n依赖：不依赖于任何库和框架\r\n\r\n## HTML5 API\r\n使用HTML5 Geolocation API的getCurrentPosition() 方法能够获取用户的经纬度信息。\r\n\r\ngetCurrentPosition() 常用参数有两个，一个是成功时执行，一个时错误处理。如果getCurrentPosition()运行成功，则向第一个参数中规定的函数返回一个coordinates对象，用于提供位置信息。\r\n\r\ncoordinates对象属性如下：\r\n\r\n| 属性    | 描述   |\r\n| :------------- | :------------- |\r\n| coords.latitude| 十进制数的纬度     |\r\n| coords.longitude| 十进制数的经度     |\r\n| coords.accuracy\t| 位置精度     |\r\n| coords.altitude\t| 海拔，海平面以上以米计     |\r\n| coords.altitudeAccuracy| 位置的海拔精度     |\r\n| coords.heading| 方向，从正北开始以度计     |\r\n| coords.speed| 速度，以米/每秒计     |\r\n| timestamp| 响应的日期/时间     |\r\n\r\n其中，latitude、longitude 以及 accuracy 属性 是固定返回的属性，其他属性在可用状态下才会一起返回。\r\n\r\n如果getCurrentPosition()运行失败，则向第二个参数中规定的函数返回一个error对象，用于提供错误信息。\r\n\r\n| 属性 |  描述|\r\n| :------------- | :------------- |\r\n| code  | 1: PERMISSION_DENIED,用户不允许地理定位 <br>2: POSITION_UNAVAILABLE,无法获取当前位置 <br>3:TIMEOUT,操作超时|\r\n|message | 返回相应的错误信息 |\r\n\r\n下例是一个简单的地理定位实例，可返回用户位置的经度和纬度:\r\n\r\n```\r\nfunction getLocation()\r\n{\r\n    if (navigator.geolocation)\r\n    {\r\n        navigator.geolocation.getCurrentPosition(showPosition, showError);\r\n    }\r\n    else\r\n    {\r\n        console.log(\"该浏览器不支持获取地理位置。\");\r\n    }\r\n}\r\n\r\nfunction showPosition(position)\r\n{\r\n    console.log(\"纬度: \" + position.coords.latitude +\r\n    \" 经度: \" + position.coords.longitude);    \r\n}\r\n\r\nfunction showError(error) {\r\n    console.log(error);\r\n}\r\n\r\n```\r\n\r\n参考资料：\r\n\r\n[菜鸟教程](http://www.runoob.com/html/html5-geolocation.html)\r\n\r\n\r\n## 百度地图API\r\n\r\n#### 命名空间\r\n\r\n百度地图API使用BMap作为命名空间，所有类均在该命名空间之下，比如：BMap.Map、BMap.Control、BMap.Overlay。\r\n\r\n#### 使用方法\r\n\r\n首先，我们需要引入百度地图API文件：\r\n```\r\n<script src=\"http://api.map.baidu.com/api?v=1.4\"></script> //参数v表示您加载API的版本，使用JavaScript APIv1.4及以前版本可使用此方式引用。\r\n\r\n<script src=\"http://api.map.baidu.com/api?v=2.0&ak=您的密钥\"></script> //使用JavaScript APIv2.0请先申请密钥ak，按此方式引用。\r\n\r\n```\r\n\r\n然后，我们再创建一个地理点坐标：\r\n\r\n```\r\nvar point = new BMap.Point(116.404, 39.915);\r\n\r\n```\r\n| 构造函数   | 描述    |\r\n| :------------- | :------------- |\r\n| Point(lng: Number, lat: Number)| 以指定的经度和纬度创建一个地理点坐标    |\r\n\r\n最后，我们可以通过创建好的点坐标获取到用户的地址解析：\r\n\r\n```\r\nvar myGeo = new BMap.Geocoder();\r\nmyGeo.getLocation(point, function (result) {\r\n    console.log(result.addressComponents.province + ' ' + result.addressComponents.city);\r\n}\r\n```\r\n\r\n| 构造函数 | 描述    |\r\n| :------------- | :------------- |\r\n| Geocoder()\t   | 创建一个地址解析器的实例      |\r\n\r\n| 方法   | 返回值     | 描述 |\r\n| :------------- | :------------- | :-------------|\r\n| getPoint(address: String, callback: Function, city: String)       | none       |对指定的地址进行解析。如果地址定位成功，则以地址所在的坐标点Point为参数调用回调函数。否则，回调函数的参数为null。city为地址所在的城市名，例如“北京市” |\r\n|getLocation(point: Point, callback: Function, options: LocationOptions)|none|对指定的坐标点进行反地址解析。如果解析成功，则回调函数的参数为GeocoderResult对象，否则回调函数的参数为null|\r\n\r\n参考资料：\r\n\r\n1. [百度地图 JavaScript API](http://lbsyun.baidu.com/index.php?title=jspopular)\r\n\r\n2. [百度地图 JavaScript API起步](http://lbsyun.baidu.com/index.php?title=jspopular/guide/helloworld)\r\n\r\n3. [百度地图 JavaScript API类参考](http://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference.html#a7b27)\r\n\r\n## 完整代码示例\r\n\r\n```\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\r\n    <title>Document</title>\r\n</head>\r\n<body>\r\n    <p id=\"test\"></p>\r\n</body>\r\n<script src=\"https://api.map.baidu.com/api?v=2.0&ak=您的密钥\" type=\"text/javascript\"></script>\r\n<script>\r\nfunction getLocation()\r\n{\r\n    if (navigator.geolocation)\r\n    {\r\n        navigator.geolocation.getCurrentPosition(showPosition, showError);\r\n    }\r\n    else\r\n    {\r\n        console.log(\"该浏览器不支持获取地理位置。\");\r\n    }\r\n}\r\n\r\nfunction showPosition(position)\r\n{\r\n    var point = new BMap.Point(position.coords.longitude, position.coords.latitude);\r\n    var myGeo = new BMap.Geocoder();\r\n    myGeo.getLocation(point, function (result) {\r\n        alert(result.addressComponents.province + ' ' + result.addressComponents.city);\r\n    })\r\n}\r\n\r\nfunction showError(error) {\r\n    console.log(error);\r\n}\r\ngetLocation();\r\n</script>\r\n</html>\r\n```\r\n\r\n## 浏览器安全机制\r\n\r\n按照上面的步骤去做，理论上是可以实现我们的功能。但事实并非如此，不信你可以起个服务验证一下看看。\r\n\r\n通过验证，你会发现在Chrome 浏览器中使用`http://localhost:8080` 或者 `http://127.0.0.1:8080` 可以正常获取到浏览器的地理位置，但通过IP或者域名的形式，如：`http://172.21.3.82:8080` 和 `http://b.cunzhang.com`进行访问时却获取不到。\r\n\r\n为什么呢？打开控制台，你会发现有以下错误信息：\r\n`Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).`\r\n\r\n“只有在安全来源的情况才才被允许”。错误信息里还包含了一个提示链接，我们不妨打开这个链接（[https://goo.gl/Y0ZkNV](https://goo.gl/Y0ZkNV)）看看。原来，为了保障用户的安全，Chrome浏览器认为只有安全的来源才能开启定位服务。那什么样才算是安全的来源呢？在打开链接的页面上有这么一段话：\r\n\r\n>“Secure origins” are origins that match at least one of the following (scheme, host, port) patterns:\r\n\r\n>* (https, \\*, \\*)\r\n>* (wss, \\*, \\*)\r\n>* (\\*, localhost, \\*)\r\n>* (\\*, 127/8, \\*)\r\n>* (\\*, ::1/128, \\*)\r\n>* (file, \\*, —)\r\n>* (chrome-extension, \\*, —)\r\n\r\n>This list may be incomplete, and may need to be changed. Please discuss!\r\n\r\n大概意思是说只有包含上述列表中的`scheme`、`host`或者`port`才会被认为是安全的来源，现在这个列表还不够完整，后续可能还会有变动，有待讨论。\r\n\r\n这就可以解释了为什么在`http://localhost:8080` 和 `http://127.0.0.1:8080`访问下可以获取到浏览器的地理位置，而在`http://172.21.3.82:8080` 和 `http://b.cunzhang.com` 确获取不到了。如果需要在域名访问的基础上实现地位位置的定位，那我们只能把`http`协议升级为`https`了。\r\n\r\n## nginx搭建https服务\r\n\r\n从`http`升级为`https`要先获取一张证书。\r\n证书是一个二进制文件，里面包含经过认证的网站公钥和一些元数据，要从经销商购买。由于我司已经升级到了`https`，就不需要我瞎折腾了，感兴趣的可以参考阮一峰老师的这篇文章《[HTTPS 升级指南](http://www.ruanyifeng.com/blog/2016/08/migrate-from-http-to-https.html)》。虽然我司对外开放的网站都已经全面升级为`https`，但是内网的测试环境还是没有升级到`https`，下面将简述一下如何通过`nginx` 来搭建一个`https`服务。\r\n\r\n#### SSL 证书\r\n\r\n要设置安全服务器，使用公共钥创建一对公私钥对。大多数情况下，发送证书请求（包括自己的公钥），你的公司证明材料以及费用到一个证书颁发机构(CA)。CA验证证书请求及您的身份，然后将证书返回给您的安全服务器。\r\n但是内网实现一个服务器端和客户端传输内容的加密，可以自己给自己颁发证书，只需要忽略掉浏览器不信任的警报即可！\r\n\r\n#### 制作CA证书\r\n\r\nXXXX.key CA私钥：\r\n\r\n    openssl genrsa -des3 -out XXXX.key 2048\r\n\r\nXXXX.crt CA根证书（公钥）：\r\n\r\n    openssl req -new -x509 -days 365 -key XXXX.key -out XXXX.crt\r\n\r\n在配置你的公钥过程中会让你填写一些信息，这时候随便填一下就可以了。\r\n\r\n\r\n#### 虚拟主机配置文件\r\n```\r\n#user  nobody;\r\nworker_processes  1;\r\n\r\n\r\nevents {\r\n    worker_connections  1024;\r\n}\r\nhttp {\r\n    include       mime.types;\r\n    default_type  application/octet-stream;\r\n    sendfile        on;\r\n    keepalive_timeout  65;\r\nupstream sslfpm {\r\n server 127.0.0.1:9000 weight=10 max_fails=3 fail_timeout=20s;\r\n}\r\nserver {\r\n listen      443;\r\nserver_name  b.cunzhang.com;\r\n #为一个server开启ssl支持\r\n ssl                  on;\r\n  #为虚拟主机指定pem格式的证书文件\r\n ssl_certificate      /etc/ssl/cunzhang_test.crt;\r\n #为虚拟主机指定私钥文件\r\n ssl_certificate_key  /etc/ssl/cunzhang_test.key;\r\n #客户端能够重复使用存储在缓存中的会话参数时间\r\n ssl_session_timeout  5m;\r\n #指定使用的ssl协议\r\n ssl_protocols  SSLv3 TLSv1;\r\n #指定许可的密码描述\r\nssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;\r\n#SSLv3和TLSv1协议的服务器密码需求优先级高于客户端密码\r\nssl_prefer_server_ciphers  on;\r\n\r\nlocation / {\r\n root  /etc/ssl/;\r\n autoindex on;\r\n       autoindex_exact_size    off;\r\n        autoindex_localtime on;\r\n}\r\n\r\n    # redirect server error pages to the static page /50x.html\r\n    #\r\n    error_page  500 502 503 504  /50x.html;\r\n    error_page  404 /404.html;\r\n\r\nlocation = /50x.html {\r\n        root  /usr/share/nginx/www;\r\n    }\r\n  location = /404.html {\r\n        root  /usr/share/nginx/www;\r\n    }\r\n\r\n    # proxy the PHP scripts to fpm\r\n    location ~ \\.php$ {\r\n access_log  /etc/ssl/ssl.access.log ;\r\n error_log /etc/ssl/ssl.error.log;\r\n root /etc/ssl/;\r\n fastcgi_param HTTPS  on;\r\n        include /usr/local/etc/nginx/fastcgi_params;\r\n        fastcgi_pass    sslfpm;\r\n    }\r\n  }\r\n}\r\n\r\n```\r\n\r\n虚拟主机文件配置之后还有记得在hosts给你的域名配置好ip地址，这样就可以通过`https`访问到你的网页实现定位功能了。\r\n\r\n参考资料：\r\n\r\n[Nginx搭建https服务器](http://www.cnblogs.com/grimm/p/5938511.html)\r\n"}}
{"actor":{"display_login":"heweixiao"}}