【问题标题】:getting users geolocation via html5 and javascript通过 html5 和 javascript 获取用户地理位置
【发布时间】:2013-01-16 13:24:27
【问题描述】:

我正在尝试通过 html5 geolcation api 获取用户的地理位置,我使用以下 sn-p:

if (navigator.geolocation) {
    var timeoutVal = 10 * 1000 * 1000;
    navigator.geolocation.getCurrentPosition(
      displayPosition, 
      displayError,
      { enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 }
    );
  }
  else {
     // DO SOME STUFF HERE
  }


  function displayPosition(position) {

    // configuration
    var myZoom = 12;
    var myMarkerIsDraggable = true;
    var myCoordsLenght = 6;
    var defaultLat = position.coords.latitude;
    var defaultLng = position.coords.longitude;
    document.getElementById('latitude').value = defaultLat;
    document.getElementById('longitude').value = defaultLng;
    /*
      1. creates the map
      2. zooms
      3. centers the map
      4. sets the map’s type
    */
    var map = new google.maps.Map(document.getElementById('canvas'), {
      zoom: myZoom,
      center: new google.maps.LatLng(defaultLat, defaultLng),
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });


    });
    // centers the map on markers coords
    map.setCenter(myMarker.position);
    // adds the marker on the map
    myMarker.setMap(map);
  }

  function displayError(error) {
    var errors = { 
      1: 'Permission denied',
      2: 'Position unavailable',
      3: 'Request timeout'
    };
    alert("Error: " + errors[error.code]);
  }
});

上述方法的问题在于,很少有用户发现它难以使用。少数情况下,他们点击了 Deny 而不是 Allow 并一直盯着屏幕。所以从可用性的角度来看,我认为一个好的方法是:

  1. 请求他们的许可。

  2. 等待 3 秒,如果他们点击拒绝或没有响应,请使用 IP 在地图上显示地理定位。

如何完成上述 sn-ps 中的第二步。 请让我知道,谢谢! 但是,有什么更好的处理方式

【问题讨论】:

  • 当用户单击拒绝或请求超时时,您的错误处理程序 (displayError()) 是否未被调用?如果它正在被调用(应该如此),那么您需要在此处执行第二步(使用用户的 IP 进行地理定位)。

标签: javascript html google-maps geolocation ip-geolocation


【解决方案1】:

这是我前段时间写的最近更新的脚本 (geolocator.js)。

更新:Geolocator v2 已发布。

特点:

  • HTML5 地理位置(通过用户许可)
  • IP 定位
  • 地理编码(地址坐标)
  • 反向地理编码(从坐标中查找地址)
  • 完整的地址信息(街道、城镇、邻里、地区、 国家、国家代码、邮政编码等)
  • 后备机制(从 HTML5 地理定位到 IP 地理查找)
  • 查看地理位置
  • 获取距离矩阵和持续时间
  • 计算两个地理点之间的距离
  • 获取时区信息
  • 获取客户端 IP
  • 支持 Google Loader(动态加载 Google 地图)
  • 动态创建 Google 地图(带有标记、信息窗口、自动调整缩放)
  • 非阻塞脚本加载(动态加载外部源,不会中断页面​​加载)

查看live demo
API documentation

用法:

var options = {
    enableHighAccuracy: true,
    timeout: 6000,
    maximumAge: 0,
    desiredAccuracy: 30, // meters
    fallbackToIP: true, // get location by IP if geolocation fails or rejected
    addressLookup: true, // requires Google API key
    timezone: true, // requires Google API key
    map: "my-map" // creates a Google map. requires Google API key
};
geolocator.locate(options, function (err, location) {
    console.log(err || location);
});

示例输出:

{
    coords: {
        latitude: 37.4224764,
        longitude: -122.0842499,
        accuracy: 30,
        altitude: null,
        altitudeAccuracy: null,
        heading: null,
        speed: null
    },
    address: {
        commonName: "",
        street: "Amphitheatre Pkwy",
        route: "Amphitheatre Pkwy",
        streetNumber: "1600",
        neighborhood: "",
        town: "",
        city: "Mountain View",
        region: "Santa Clara County",
        state: "California",
        stateCode: "CA",
        postalCode: "94043",
        country: "United States",
        countryCode: "US"
    },
    formattedAddress: "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
    type: "ROOFTOP",
    placeId: "ChIJ2eUgeAK6j4ARbn5u_wAGqWA",
    timezone: {
        id: "America/Los_Angeles",
        name: "Pacific Standard Time",
        abbr: "PST",
        dstOffset: 0,
        rawOffset: -28800
    },
    flag: "//cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.3.1/flags/4x3/us.svg",
    map: {
        element: HTMLElement,
        instance: Object, // google.maps.Map
        marker: Object, // google.maps.Marker
        infoWindow: Object, // google.maps.InfoWindow
        options: Object // map options
    },
    timestamp: 1456795956380
}

【讨论】:

    【解决方案2】:

    如果超时或用户拒绝请求,我会设置一个默认位置,例如纽约,纽约 (40.7142, -74.0064)。如果用户拒绝请求,他们还必须预料到您不会知道他们的位置,因此选择一个可理解的默认值是下一个最佳选择。

    通过在两个地方调用displayPosition({coords: {latitude: 40.7142, longitude: -74.0064}}) 可以在不更改代码的情况下使用默认值:

    if (navigator.geolocation) {
        var timeoutVal = 10 * 1000 * 1000;
        navigator.geolocation.getCurrentPosition(
            displayPosition, 
            displayError,
            { enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 }
        );
    }
    else {
        displayPosition({coords: {latitude: 40.7142, longitude: -74.0064}})
    }
    ....
    function handleError(error){
        switch(error.code)
        {
            case error.PERMISSION_DENIED: alert("User did not share geolocation data");break;  
            case error.POSITION_UNAVAILABLE: alert("Could not detect current position");break;  
            case error.TIMEOUT: alert("Retrieving position timed out");break;  
            default: alert("Unknown Error");break;  
        }
        displayPosition({coords: {latitude: 40.7142, longitude: -74.0064}});
    }
    

    http://nearbytweets.com 上,我使用“队列”函数来查找用户的位置,循环遍历队列,直到其中一个找到有效位置。最后一个函数返回纽约,纽约,这意味着所有其他尝试都失败了。以下是稍作修改的代码示例:

    var position_finders = [                                                                                                                                                                                                              
        function () {
            if (navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(check_position, check_position);
                return;
            }   
            check_position();
        },  
        function () {
            check_position(JSON.parse(localStorage.getItem('last_location')));
        },  
        function () {
            $.ajax({
                url: 'http://www.google.com/jsapi?key=' + google_api_key,
                dataType: 'script',
                success: check_position
            }); 
        },  
        function () {
            check_position({latitude: 40.7142, longitude: -74.0064}, true);
        }   
    ],
    
    check_position = function (pos, failed) {
        pos = pos || {}; 
        pos = pos.coords ? 
            pos.coords :
            pos.loader ?
            pos.loader.clientLocation :
            pos;
    
        if (typeof pos.latitude === 'undefined' || typeof pos.longitude === 'undefined') {
            position_finders.shift()();
            return;
        }   
    
        localStorage.setItem('last_location', JSON.stringify(pos));
    
        // using your code, I would call this:
        displayPosition(pos);
    };
    
    check_position();
    

    每个 position_finder 的作用如下:

    1. 尝试 navigator.geolocation。
    2. 尝试从 localStorage 中提取他们的最后一个位置
    3. 使用Google Loader通过IP查找位置
    4. 使用纽约州纽约市

    【讨论】:

      【解决方案3】:

      从 UI 的角度来看,我会遵循以下步骤:

      A) 显示一个漂亮的文本框,解释接下来会发生什么(即“浏览器将要求您授予权限”、“单击允许”等)并要求按下按钮继续 B) 像现在一样显示地图

      【讨论】:

        【解决方案4】:

        您可以使用此在线服务轻松获取 lat lng:

        http://dev.maxmind.com/geoip/javascript

        关于超时,我认为没有办法干扰浏览器的权限机制(例如,在一定秒数后关闭该权限弹出窗口)——尽管我很乐意被证明是错误的。您可以做的是设置一个计时器,并在三秒后获取基于 IP 的地理位置并将地图设置为它(或者,在 3 秒后刷新页面,并设置一个触发基于 IP 的地理而不是 HTML5 的 cookie geo,但如果你问我,那有点过分了)。

        然后,如果他们允许,它将使用 HTML5 地理位置刷新地图(应该更准确)。您还可以将 IP 地理位置回退封装到一个函数中,如果他们没有 HTML5 地理位置或者他们点击了拒绝,则可以使用它。

        这是一个小提琴: http://jsfiddle.net/mfNCn/1/

        这是小提琴的粗略剪辑:

        <script src="http://j.maxmind.com/app/geoip.js" charset="ISO-8859-1" type="text/javascript"></script>
        ...
        var time_perm = window.setTimeout(get_by_ip, 3000);
        ...
        function get_by_ip() {
            var lat = geoip_latitude();
            var lng = geoip_longitude();
            map_it(lat, lng);
        }
        ...
        function map_it(lat,lng) {
            // build your map here
        }
        

        (我犹豫把整个代码块放在这里,因为它相当长,所以检查小提琴的其余部分和完整的实现)

        【讨论】:

          【解决方案5】:

          这可能会有所帮助: http://jsfiddle.net/sandesh2302/FghFZ/ 我用这个做我的东西,效果很好。

          例如:

              <!DOCTYPE html>
          <html>
            <head>
              <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />    
              <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=true"></script>    
          
              <script type="text/javascript">
                function getLocation(){
                  navigator.geolocation.getCurrentPosition(handleSuccess,handleError);
                }
          
                function initiate_watchlocation() {  
                  if(watchProcess == null){
                    watchProcess = navigator.geolocation.watchPosition(handleSuccess,handleError);
                  }
                } 
          
                function stop_watchlocation() {  
                  if(watchProcess != null){
                    navigator.geolocation.clearWatch(watchProcess);
                  }
                } 
          
                function handleSuccess(position){
                  drawMap(position);
                }
          
                function handleError(error){
                  switch(error.code)
                  {
                    case error.PERMISSION_DENIED: alert("User did not share geolocation data");break;  
                    case error.POSITION_UNAVAILABLE: alert("Could not detect current position");break;  
                    case error.TIMEOUT: alert("Retrieving position timed out");break;  
                    default: alert("Unknown Error");break;  
                  }
                }
          
          
                function drawMap(position) {
                  var container = $('#map_canvas');
                  var myLatLong = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
                  var mapOptions = {
                    center: myLatLong,
                    zoom: 12,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                  };
                  var map = new google.maps.Map(container[0],mapOptions);
                  container.css('display','block');
                  var marker = new google.maps.Marker({ 
                    position: myLatLong,
                    map:map,
                    title:"My Position (Accuracy of Position: " + position.coords.accuracy + " Meters), Altitude: " 
                      + position.coords.altitude + ' Altitude Accuracy: ' + position.coords.altitudeAccuracy
                  });
                }
          
                function drawStaticMap(position){
                  var container = $('#map_canvas');
                  var imageUrl = "http://maps.google.com/maps/api/staticmap?sensor=false&center=" + position.coords.latitude + "," +  
                              position.coords.longitude + "&zoom=18&size=640x500&markers=color:blue|label:S|" +  
                              position.coords.latitude + ',' + position.coords.longitude;  
          
                  container.css({
                    'display':'block',
                    'width' : 640
                  });
                  $('<img/>',{
                    src : imageUrl
                  }).appendTo(container);
                } 
              </script>
            </head>
            <body >
              <button id="getLocation">Find My Location</button>
              <div style="text-align:center">
                <button id="initWatch">Put Watch on Your Position</button>
                <button id="stopWatch">Stop Position Watching</button>
              </div>
              <div id="map_canvas" ></div>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
              tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
              quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
              consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
              cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
              proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
          
          
          
            </body>
          </html>
          

          【讨论】:

            【解决方案6】:

            好的,这不是代码答案,更多的是用户体验答案。

            从用户体验的角度来看,最突出的第一件事是在您触发浏览器请求他们许可之前您提供的信息不足。

            我建议您使用某种覆盖框来显示屏幕截图(上面带有一个大箭头),以显示屏幕上他们将被要求获得许可的“位置”。您也可以借此机会告诉他们,如果他们拒绝许可或在 10 秒内未能接受许可(即他们忽略提示栏)会发生什么。

            我建议您不要默认显示 IP 位置,因为他们基本上“可能会说”我不同意让您知道我在哪里。然后你显示他们在哪里的大地图,这可能会吓坏一些点击拒绝的人!此外,它可能非常不准确。

            “不请求许可请求原谅”的想法可能适用于 Biz dev,但不适用于 UI,因为它们只是不会回来。

            我会问自己,您是否真的也需要高精度,因为它会耗尽用户电池,花费更长的时间,并且可能不会给您带来更多的收益,特别是如果您只需要它来粗略地找出它们的位置.如果需要,您可以随时再次调用它以获得更准确的读数。

            如果他们不点击拒绝或允许则超时的概念可以通过 setTimeout 来实现。因此,一旦他们在您的叠加框上单击“好的,我已准备好单击允许”,请启动超时,如果最终超时,则按照您在上述步骤中告诉他们的操作。

            通过使用此方法,您可以强制用户允许或拒绝(忽略),无论哪种方式,它都会将控制权交还给您,并让用户完全了解情况。

            虽然这不是特定于代码的答案,但从您的 JS 中可以清楚地看出,“代码实现帮助”并不是这里真正的问题。我希望这能让您更多地考虑您的用户体验。

            【讨论】:

            • 说得很好。我想支持关于在他们最初拒绝显示他们的位置时默认显示 IP 位置的观点 - 这肯定会让用户感到“不安全”访问您的网站,因为他们不一定知道您使用 IP 来进行地理定位一旦他们拒绝 HTML5 地理位置服务。
            • 我实际上已经对一些免费的 IP 到地理定位服务进行了评估,并在地图上比较了它们的准确性。如果您决定使用服务,可以在这里查看:svignara.github.com/geolocation.html 您可以在 github 上查看我的代码库,了解如何向这些服务发出 ajax 请求。
            【解决方案7】:

            【讨论】:

            • 我想要一个 webapp 的答案。
            【解决方案8】:

            然后您将使用像这样的地理 ip api:

            http://freegeoip.net/static/index.html

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-03-13
              • 1970-01-01
              • 1970-01-01
              • 2016-08-21
              • 2012-03-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多