<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      php LBS(附近地理位置)功能實現的一些思路

      在開發中經常會遇到把數據庫已有經緯度的地方進行距離排序然后返回給用戶
      例如一些外賣app打開會返回附近的商店,這個是怎么做到的呢?

      思路一:

      根據用戶當前的位置,用計算經緯度距離的算法逐一計算比對距離,然后進行排序。這里可以參考下面這個算法:

      <?php
      /**
       * 查找兩個經緯度之間的距離
       *
       * @param  $latitude1   float  起始緯度
       * @param  $longitude1  float  起始經度
       * @param  $latitude2   float  目標緯度
       * @param  $longitude2  float  目標經度
       * @return array(miles=>英里,feet=>英尺,yards=>碼,kilometers=>公里,meters=>米)
       * @example
       *
       *         $point1 = array('lat' => 40.770623, 'long' => -73.964367);
       *         $point2 = array('lat' => 40.758224, 'long' => -73.917404);
       *         $distance = getDistanceBetweenPointsNew($point1['lat'], $point1['long'], $point2['lat'], $point2['long']);
       *         foreach ($distance as $unit => $value) {
       *             echo $unit.': '.number_format($value,4);
       *         }
       *
       *         The example returns the following:
       *
       *         miles: 2.6025       //英里
       *         feet: 13,741.4350   //英尺
       *         yards: 4,580.4783   //碼
       *         kilometers: 4.1884  //公里
       *         meters: 4,188.3894  //米
       *
       */
      function getDistanceBetweenPointsNew($latitude1, $longitude1, $latitude2, $longitude2) {
          $theta = $longitude1 - $longitude2;
          $miles = (sin(deg2rad($latitude1)) * sin(deg2rad($latitude2))) + (cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * cos(deg2rad($theta)));
          $miles = acos($miles);
          $miles = rad2deg($miles);
          $miles = $miles * 60 * 1.1515;
          $feet = $miles * 5280;
          $yards = $feet / 3;
          $kilometers = $miles * 1.609344;
          $meters = $kilometers * 1000;
          return compact('miles', 'feet', 'yards', 'kilometers', 'meters');
      }
      ?>
      

      這個思路是要每次都獲取全部數據,然后進行不斷的循環計算,對于大數據量來說簡直是噩夢。

      思路二:

      利用二維的經緯度轉換成一維的數據,然后直接sql查詢,無須一一比對。
      例如經緯度 110.993736,21.495705 => w7yfm9pjt9b4
      這就是geohash算法,這里簡單說一下,geohash是一種地理位置編碼,通過數學的方法進行一定的轉換,使其與經緯度對應,變成一串可比對的字符串。
      這里不做深入的了解,大概知道一下就好,轉換出來的編碼有一定的規律,例如同一個省份的前幾位字符是一樣的,字符數相似越多,證明距離越近。類似于公民身份證一樣。
      有興趣的可以自行搜索了解一下。
      下面直接給出轉換的php代碼

      <?php
      /**
       * Encode and decode geohashes
       *
       */
      class Geohash {
          private $coding = "0123456789bcdefghjkmnpqrstuvwxyz";
          private $codingMap = array();
      
          public function Geohash() {
              //build map from encoding char to 0 padded bitfield
              for ($i = 0; $i < 32; $i++) {
                  $this->codingMap[substr($this->coding, $i, 1)] = str_pad(decbin($i), 5, "0", STR_PAD_LEFT);
              }
      
          }
      
          /**
           * Decode a geohash and return an array with decimal lat,long in it
           */
          public function decode($hash) {
              //decode hash into binary string
              $binary = "";
              $hl = strlen($hash);
              for ($i = 0; $i < $hl; $i++) {
                  $binary .= $this->codingMap[substr($hash, $i, 1)];
              }
      
              //split the binary into lat and log binary strings
              $bl = strlen($binary);
              $blat = "";
              $blong = "";
              for ($i = 0; $i < $bl; $i++) {
                  if ($i % 2) {
                      $blat = $blat . substr($binary, $i, 1);
                  } else {
                      $blong = $blong . substr($binary, $i, 1);
                  }
      
              }
      
              //now concert to decimal
              $lat = $this->binDecode($blat, -90, 90);
              $long = $this->binDecode($blong, -180, 180);
      
              //figure out how precise the bit count makes this calculation
              $latErr = $this->calcError(strlen($blat), -90, 90);
              $longErr = $this->calcError(strlen($blong), -180, 180);
      
              //how many decimal places should we use? There's a little art to
              //this to ensure I get the same roundings as geohash.org
              $latPlaces = max(1, -round(log10($latErr))) - 1;
              $longPlaces = max(1, -round(log10($longErr))) - 1;
      
              //round it
              $lat = round($lat, $latPlaces);
              $long = round($long, $longPlaces);
      
              return array($lat, $long);
          }
      
          /**
           * Encode a hash from given lat and long
           */
          public function encode($lat, $long) {
              //how many bits does latitude need?
              $plat = $this->precision($lat);
              $latbits = 1;
              $err = 45;
              while ($err > $plat) {
                  $latbits++;
                  $err /= 2;
              }
      
              //how many bits does longitude need?
              $plong = $this->precision($long);
              $longbits = 1;
              $err = 90;
              while ($err > $plong) {
                  $longbits++;
                  $err /= 2;
              }
      
              //bit counts need to be equal
              $bits = max($latbits, $longbits);
      
              //as the hash create bits in groups of 5, lets not
              //waste any bits - lets bulk it up to a multiple of 5
              //and favour the longitude for any odd bits
              $longbits = $bits;
              $latbits = $bits;
              $addlong = 1;
              while (($longbits + $latbits) % 5 != 0) {
                  $longbits += $addlong;
                  $latbits += !$addlong;
                  $addlong = !$addlong;
              }
      
              //encode each as binary string
              $blat = $this->binEncode($lat, -90, 90, $latbits);
              $blong = $this->binEncode($long, -180, 180, $longbits);
      
              //merge lat and long together
              $binary = "";
              $uselong = 1;
              while (strlen($blat) + strlen($blong)) {
                  if ($uselong) {
                      $binary = $binary . substr($blong, 0, 1);
                      $blong = substr($blong, 1);
                  } else {
                      $binary = $binary . substr($blat, 0, 1);
                      $blat = substr($blat, 1);
                  }
                  $uselong = !$uselong;
              }
      
              //convert binary string to hash
              $hash = "";
              for ($i = 0; $i < strlen($binary); $i += 5) {
                  $n = bindec(substr($binary, $i, 5));
                  $hash = $hash . $this->coding[$n];
              }
      
              return $hash;
          }
      
          /**
           * What's the maximum error for $bits bits covering a range $min to $max
           */
          private function calcError($bits, $min, $max) {
              $err = ($max - $min) / 2;
              while ($bits--) {
                  $err /= 2;
              }
      
              return $err;
          }
      
          /*
           * returns precision of number
           * precision of 42 is 0.5
           * precision of 42.4 is 0.05
           * precision of 42.41 is 0.005 etc
           */
          private function precision($number) {
              $precision = 0;
              $pt = strpos($number, '.');
              if ($pt !== false) {
                  $precision = -(strlen($number) - $pt - 1);
              }
      
              return pow(10, $precision) / 2;
          }
      
          /**
           * create binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
           * removing the tail recursion is left an exercise for the reader
           */
          private function binEncode($number, $min, $max, $bitcount) {
              if ($bitcount == 0) {
                  return "";
              }
      
              #echo "$bitcount: $min $max<br>";
      
              //this is our mid point - we will produce a bit to say
              //whether $number is above or below this mid point
              $mid = ($min + $max) / 2;
              if ($number > $mid) {
                  return "1" . $this->binEncode($number, $mid, $max, $bitcount - 1);
              } else {
                  return "0" . $this->binEncode($number, $min, $mid, $bitcount - 1);
              }
      
          }
      
          /**
           * decodes binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
           * removing the tail recursion is left an exercise for the reader
           */
          private function binDecode($binary, $min, $max) {
              $mid = ($min + $max) / 2;
      
              if (strlen($binary) == 0) {
                  return $mid;
              }
      
              $bit = substr($binary, 0, 1);
              $binary = substr($binary, 1);
      
              if ($bit == 1) {
                  return $this->binDecode($binary, $mid, $max);
              } else {
                  return $this->binDecode($binary, $min, $mid);
              }
      
          }
      }
      ?>
      

      把每一個經緯度都轉換成geohash編碼并儲存起來,比對的時候直接sql

      $sql = 'select * from xxx where geohash like "'.$like_geohash.'%"';
      

      這里like_geohash位數越多說明越精確。
      下面是geohash經度距離換算關系,比如geohash如果有7位數,說明范圍在76米左右,八位數則是19米,可以根據這個進行查詢。

      geohash長度 Lat位數 Lng位數 Lat誤差 Lng誤差 km誤差
      1 2 3 ±23 ±23 ±2500
      2 5 5 ± 2.8 ±5.6 ±630
      3 7 8 ± 0.70 ± 0.7 ±78
      4 10 10 ± 0.087 ± 0.18 ±20
      5 12 13 ± 0.022 ± 0.022 ±2.4
      6 15 15 ± 0.0027 ± 0.0055 ±0.61
      7 17 18 ±0.00068 ±0.00068 ±0.076
      8 20 20 ±0.000086 ±0.000172 ±0.01911
      9 22 23 ±0.000021 ±0.000021 ±0.00478
      10 25 25 ±0.00000268 ±0.00000536 ±0.0005971
      11 27 28 ±0.00000067 ±0.00000067 ±0.0001492
      12 30 30 ±0.00000008 ±0.00000017 ±0.0000186

      這個思路明顯優于第一個思路,且查詢起來速度非常快,也不用管有多大的數據,直接在數據庫里面進行like查詢就好,不過要做好索引才行,缺點也是比較明顯
      無法控制想要的精確訪問,對于返回的數據無法進行距離的先后排序,不過已經能滿足一定的需求,后期再結合思路一也可以做到距離的先后。

      思路三:

      前面兩種方法都是通過很生硬的數學方法進行比對,所計算的也都是直線距離,但是現實并不是數學那樣理想。
      現實中兩個很靠近的經緯度中間也有可能隔著一條跨不過去的河導致要繞很遠的路,這時就要考慮實際情況。
      很慶幸有些地圖廠商已經幫我們考慮到了,所以還可以借助第三方api。
      這里簡單說一下高德地圖的[ 云圖服務API ]()
      1、注冊高德地圖賬戶,并申請云圖key。
      2、創建云地圖,也就是把你現在的數據放到高德地圖上 [ 云圖存儲API ](),這里可以手動創建也能調用相關的api創建。
      可以把數據導出excel然后批量上傳,當然后期如果要新增盡量還是用它提供的接口進行增量添加。創建完成大概會生成這樣一張表,有tableid,這個后面查詢接口需要使用,字段可以自定義,方便業務邏輯。

      3、使用高德api進行查詢你的云地圖 [ 數據檢索 ]() ,這里使用周邊檢索,可以根據你當前的位置進行檢索。
      過程其實也不復雜,就是把數據放到高德,高德幫你完成了距離的排序,當然它提供的是比較實際的距離。具體實現需要研究一下高德提供的接口。
      這個思路可以解決精準度問題,但開發成本大,還要跑一遍第三方去獲取數據,可能會犧牲一定效率,具體取舍,仁者見仁吧。

      posted @ 2018-05-23 18:28  Ariphan  閱讀(2059)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久一级精品久熟女人妻| 精品亚洲综合一区二区三区| 丰满人妻AV无码一区二区三区| 国产中文字幕在线一区| 极品粉嫩小泬无遮挡20p| 亚洲人成网站在线无码| 国产精品中文字幕一区| 国产无遮挡又黄又爽不要vip软件| 欧美日韩精品一区二区视频| 亚洲中文字幕人妻系列| 久久精品人妻无码一区二区三区| 国产精品美女网站| 中文字幕国产精品自拍| 日韩av裸体在线播放| 国产成人精品白浆免费视频试看 | 狠狠五月深爱婷婷网| 亚洲国产天堂一区二区三区| 亚洲国产精品久久久久秋霞| 亚洲情A成黄在线观看动漫尤物| 国产性色av高清在线观看| 国产成人精品久久一区二区| 视频专区熟女人妻第二页| 国产粉嫩一区二区三区av| 武装少女在线观看高清完整版免费 | 丰满高跟丝袜老熟女久久| 亚洲另类无码一区二区三区 | 国产自拍在线一区二区三区 | 国产免费午夜福利在线观看 | 亚洲精品成人一二三专区| 亚洲偷自拍国综合| 亚洲热无码av一区二区东京热av| 成 人 色 网 站免费观看| 人妻系列无码专区69影院| 少妇人妻av毛片在线看| 九九热精彩视频在线免费| 亚洲精品日韩久久精品| 久久亚洲精品情侣| 深夜福利啪啪片| 免费无遮挡无码永久在线观看视频| 亚洲精品自拍视频在线看| 国产福利酱国产一区二区|