SatelliteLocationTransform.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Mead
  5. * Date: 2019/9/3
  6. * Time: 8:05 PM
  7. */
  8. namespace App\Servers\Weikemu\Transforms;
  9. use App\Handlers\BikeStatusInfoSyncHandler;
  10. use App\Handlers\ConvertHandler;
  11. use App\Handlers\MapHandler;
  12. use App\Maps\BikeMap;
  13. use App\Models\AreaTraitModel;
  14. use App\Models\BatteryTraitModel;
  15. use App\Models\BoxSettingTraitModel;
  16. use App\Models\LocationLosTraitModel;
  17. use App\Models\OrderTraitModel;
  18. use App\Models\WarningLogTraitModel;
  19. use App\Servers\BaseServer;
  20. use App\Servers\Weikemu\BikeControl;
  21. use App\Servers\Weikemu\Maps\VideoMap;
  22. /**
  23. * 卫星位置报文
  24. * Class LoginServer
  25. * @package App\Servers
  26. */
  27. class SatelliteLocationTransform extends BaseServer
  28. {
  29. use BoxSettingTraitModel, WarningLogTraitModel, BatteryTraitModel, LocationLosTraitModel, OrderTraitModel, AreaTraitModel;
  30. public function main($body)
  31. {
  32. $data = $this->decode($body);
  33. // 位置信息
  34. $bike_no = $_SESSION['bike_no'];
  35. $box_no = $data['box_no'];
  36. if (!$bike_no) return [];
  37. $location = [
  38. 'order_id' => 0,
  39. 'bike_id' => 0,
  40. 'area_id' => 0,
  41. 'type' => 'no',
  42. 'is_rent' => 0
  43. ];
  44. $battery_power = $data['battery_voltage'];
  45. $i = count($data['locations']);
  46. $last_location = [
  47. 'lat' => $data['locations'][$i - 1]['location_latitude'],
  48. 'lng' => $data['locations'][$i - 1]['location_longitude'],
  49. ];
  50. $last_location = (new MapHandler())->wgs84togcj02($last_location['lng'], $last_location['lat']);
  51. $last_location = [
  52. 'lat' => $last_location[1],
  53. 'lng' => $last_location[0],
  54. ];
  55. $cols = [];
  56. // 验证位置是否正常
  57. if ($last_location['lat'] > 0) {
  58. $cols['last_location'] = json_encode($last_location, true);
  59. $cols['last_location_time'] = date('Y-m-d H:i:s');
  60. } else {
  61. //位置数据为0,说明车可能在房内
  62. if ($this->is_throw_num_time($data['box_no'], 'location_error', 10, 60)) {
  63. // 发出警告
  64. self::warningLocationError($bike_no, $box_no, $data, $body, "location");
  65. }
  66. return $this->response();
  67. }
  68. if ($data['status']['order_status']) {
  69. // 电车打开
  70. // 获取用户信息
  71. $order = (new BikeStatusInfoSyncHandler($this->redis))->getRideBikeOrderInfo($bike_no);
  72. if ($order) {
  73. //是否有电量信息
  74. if (array_key_exists('is_rent', $order)) {
  75. $location['is_rent'] = $order['is_rent'];
  76. }
  77. // 订单信息
  78. if (array_key_exists('id', $order)) {
  79. $location['order_id'] = $order['id'];
  80. $location['area_id'] = $order['area_id'];
  81. }
  82. if (array_key_exists('bike_id', $order)) {
  83. $location['bike_id'] = $order['bike_id'];
  84. }
  85. $location['type'] = BikeStatusInfoSyncHandler::ROLE_USER;
  86. if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_USER) {
  87. if ($location['is_rent']) {
  88. //租车
  89. if ($order['is_close_bike'] && $data['status']['bike_lock'] == 1) {
  90. //关车之后没有关上,强制锁车
  91. self::log($box_no, 'LOCATION_FOUCE_CLOSE_BIKE', self::$LOG_COMMON);
  92. BikeControl::closeLock($data['box_no']);
  93. }
  94. } else {
  95. // 临时停车没有关锁
  96. if ($order['is_temporary_close'] && $data['status']['bike_lock'] == 1) {
  97. self::log($box_no, 'LOCATION_FOUCE_TEMPORARY_CLOSE_BIKE', self::$LOG_COMMON);
  98. BikeControl::temporaryCloseLock($data['box_no']);
  99. }
  100. }
  101. //用户正常骑行订单
  102. if ($last_location['lat'] > 0) {//判断是否在骑行区域内
  103. // 判断后台是否设置超速区域播报语音
  104. if ($order['is_out_area_lost_electric']) {
  105. $is_out_status = $this->isOutArea($last_location['lat'], $last_location['lng'], $order['area_id'], $data['box_no']);
  106. if (!$is_out_status['is_out_area']) {//播报语言
  107. self::log('超区域');
  108. if ($this->is_throw_num_time($data['box_no'], 'is_out_area_nearby', 3, 0.5)) {
  109. self::log('播报语音');
  110. if (!$is_out_status['is_out_area_nearby']) {
  111. //断电播放
  112. BikeControl::playVoice($data['box_no'], VideoMap::VIDEO_BATTERY_EDGE);
  113. } else {
  114. //超出运营区
  115. BikeControl::playVoice($data['box_no'], VideoMap::VIDEO_GO_BEYOND);
  116. }
  117. }
  118. if (!$is_out_status['is_out_area_nearby']) {
  119. //失能
  120. echo '要失能';
  121. if ($this->is_throw_num_time($data['box_no'], 'is_out_area_nearby', 4, 1)) {
  122. if ((!$this->redis->exists('bike_out_area_open_electric_' . $bike_no)) || $data['status']['bike_lock']) {
  123. echo '失能';
  124. self::log($box_no, 'LOCATION_BIKE_OUT_AREA_LOSE', self::$LOG_COMMON);
  125. BikeControl::outAreaLoseElectric($data['box_no'], VideoMap::VIDEO_POWER_FAILURE);
  126. $this->redis->set('bike_out_area_' . $bike_no, 1, 60);
  127. }
  128. }
  129. } else {
  130. //供能
  131. if ($this->redis->exists('bike_out_area_' . $bike_no)) {
  132. self::log($box_no, 'LOCATION_BIKE_OUT_AREA_ADD', self::$LOG_COMMON);
  133. BikeControl::outAreaGetElectric($data['box_no']);
  134. $this->redis->del('bike_out_area' . $bike_no);
  135. $this->redis->set('bike_out_area_open_electric_' . $bike_no, 1, 120);
  136. }
  137. }
  138. } else {
  139. //供能
  140. if ($this->redis->exists('bike_out_area_' . $bike_no)) {
  141. self::log($box_no, 'LOCATION_BIKE_OUT_AREA_ADD', self::$LOG_COMMON);
  142. BikeControl::outAreaGetElectric($data['box_no']);
  143. $this->redis->del('bike_out_area' . $bike_no);
  144. $this->redis->set('bike_out_area_open_electric_' . $bike_no, 1, 120);
  145. }
  146. }
  147. }
  148. }
  149. //电量过低自动关车
  150. //是否低电关电车
  151. if ($order['is_low_electric_close_bike']) {
  152. if ($battery_power <= 40) {
  153. if ($this->is_throw_num_time($data['box_no'], 'is_out_area_nearby', 3, 0.5)) {
  154. self::log('低电语音');
  155. BikeControl::playVoice($data['box_no'], VideoMap::VIDEO_LOW_POWER);
  156. }
  157. if ($this->is_throw_num_time($data['box_no'], 'battery_low', 20, 5)) {
  158. $status = json_decode(file_get_contents(Config['close_order_api_url'] . "&box_no={$box_no}&bike_no={$bike_no}&type=电量低"), true);
  159. self::log($box_no, 'LOCATION_BATTERY_LOW_AUTO_ORDER', self::$LOG_COMMON);
  160. }
  161. }
  162. }
  163. } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_WORKER) {
  164. //运维骑行订单
  165. // 订单信息
  166. $location['type'] = BikeStatusInfoSyncHandler::ROLE_WORKER;
  167. } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_BIND) {
  168. $location['type'] = BikeStatusInfoSyncHandler::ROLE_BIND;
  169. } else if ($order['role'] === BikeStatusInfoSyncHandler::ROLE_SERVER) {
  170. //系统本身操作
  171. $location['type'] = BikeStatusInfoSyncHandler::ROLE_SERVER;
  172. } else {
  173. self::log($box_no, 'LOCATION_CLOSE_BIKE_NO_USER_TYPE', self::$LOG_COMMON);
  174. BikeControl::closeLock($data['box_no']);
  175. }
  176. } else {
  177. //处理晚到的数据
  178. $next_log = $this->byTimeGetOrder($data['box_time'], $box_no);
  179. if ($next_log) {
  180. $re = $this->byIdAndIsRentAndTime($next_log['order_id'], $next_log['is_rent'], $data['box_time']);
  181. if ($re) {
  182. $location['is_rent'] = $next_log['is_rent'];
  183. $location['order_id'] = $next_log['order_id'];
  184. $location['area_id'] = $next_log['area_id'];
  185. $location['bike_id'] = $next_log['bike_id'];
  186. $location['type'] = BikeStatusInfoSyncHandler::ROLE_USER;
  187. }
  188. } else {
  189. //非法骑行
  190. $num = $this->redis->incr('bike:illegal:open:' . $bike_no, 1);
  191. $this->redis->expire('bike:illegal:open:' . $bike_no, 120);
  192. self::log($box_no, 'LOCATION_CLOSE_BIKE_NO_ORDER', self::$LOG_COMMON);
  193. BikeControl::closeLock($data['box_no']);
  194. if ($num > 5) {
  195. if (!$this->is_ep_min($box_no, 'illegal_location', 20)) {
  196. $this->warningFF($bike_no, $data['box_no'], $data, $body);
  197. }
  198. }
  199. }
  200. }
  201. if (!$this->is_ep_min($box_no, 'update_battery', 5)) {
  202. $cols['battery_power'] = $battery_power;
  203. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK;
  204. if ($cols['battery_power'] <= self::$max_ride_v) {
  205. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW;
  206. //30分钟插一次报警信息
  207. if (!$this->is_ep_min($box_no, 'warning_log_battery', 30)) {
  208. $this->warningLogBatteryLow($bike_no, $data['box_no'], ['battery_power' => $cols['battery_power']], $body, 'location');
  209. }
  210. }
  211. }
  212. // 更新车的位置信息(非骑行状态)
  213. if (count($cols) && !$this->is_ep_min($box_no, 'update_ride_bike_location', 1)) {
  214. $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query();
  215. }
  216. } else {
  217. // 车静止状态
  218. if ($data['status']['bike_lock']) {
  219. BikeControl::closeLock($data['box_no']);
  220. }
  221. if (!$this->is_ep_min($box_no, 'update_stop_bike_battery', 15)) {
  222. $cols['battery_power'] = $battery_power;
  223. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_OK;
  224. if ($cols['battery_power'] <= self::$max_ride_v) {
  225. $cols['is_low_battery_power'] = BikeMap::BATTERY_POWER_LOW;
  226. //30分钟插一次报警信息
  227. // if (!$this->is_ep_min($box_no, 'warning_log_battery', 30)) {
  228. $this->warningLogBatteryLow($bike_no, $data['box_no'], ['battery_power' => $cols['battery_power']], $body, 'location');
  229. // }
  230. }
  231. }
  232. // 更新车的位置信息(非骑行状态)
  233. if (count($cols) && !$this->is_ep_min($box_no, 'update_bike_location', 10)) {
  234. $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query();
  235. } else {
  236. //换电池及时更新电量
  237. $openBatteryKey = "cache:open_battery:{$box_no}";
  238. if ($this->redis->exists($openBatteryKey)) {
  239. $this->db->update('bikes')->where('box_no = ' . $data['box_no'])->cols($cols)->query();
  240. }
  241. }
  242. if (($last_location['lat'] > 0)) {
  243. // 修改车的位置
  244. $is_location_ex = $this->redis->geopos(BikeStatusInfoSyncHandler::REDIS_BIKE_LOCATION_TAG, $bike_no)[0];
  245. if ((count($is_location_ex) !== 0)) {
  246. $this->redis->geoadd(BikeStatusInfoSyncHandler::REDIS_BIKE_LOCATION_TAG, $last_location['lng'], $last_location['lat'], $bike_no);
  247. }
  248. }
  249. }
  250. foreach ($data['locations'] as $v) {
  251. $lnglat = (new MapHandler())->wgs84togcj02($v['location_longitude'], $v['location_latitude']);
  252. $this->mongo->location_logs->insertOne([
  253. 'bike_no' => $bike_no,
  254. 'box_no' => $data['box_no'],
  255. 'order_id' => $location['order_id'],
  256. 'bike_id' => $location['bike_id'],
  257. 'area_id' => $location['area_id'],
  258. 'latitude' => $lnglat[1],
  259. 'longitude' => $lnglat[0],
  260. 'speed' => $v['location_speed']['speed'],
  261. 'battery_power' => $battery_power,
  262. 'mileage' => $data['kilometers'] ?? 0,
  263. 'is_riding' => $data['status']['order_status'] ?? 0,
  264. 'is_yundong' => $data['status']['rear_wheel_motion'] ?? 0,
  265. 'type' => $location['type'],
  266. 'is_rent' => $location['is_rent'],
  267. 'created_at' => date('Y-m-d H:i:s'),
  268. 'box_time' => $data['box_time'],
  269. 'status' => 1
  270. ]);
  271. }
  272. return $this->response();
  273. }
  274. /**
  275. * 状态响应
  276. * @param $login_type
  277. * @return array
  278. * User: Mead
  279. */
  280. public function response($login_type = '00')
  281. {
  282. $body = [
  283. '5b',
  284. '0000'
  285. ];
  286. return $body;
  287. }
  288. /**
  289. * 解析装载的状态消息
  290. * @param $body
  291. * @return array
  292. * User: Mead
  293. */
  294. private function decode($body)
  295. {
  296. $top34Data = $this->decodeWeiKeMuTop34($body);
  297. $i = 34;
  298. // 点类型 与 是否超速
  299. $location_type = self::stitching($body, $i, 1); // i=34
  300. $i += 1;
  301. // 信息组数
  302. $message_num = hexdec(self::stitching($body, $i, 1)); // i=35
  303. $i += 1;
  304. // 第一组样点对应时间
  305. $first_location_time = self::stitching($body, $i, 4); // i=36
  306. $i += 4;
  307. // 第一组样点对应维度
  308. $first_location_latitude = self::stitching($body, $i, 4);// i=40
  309. $i += 4;
  310. // 第一组样点对应经度
  311. $first_location_longitude = self::stitching($body, $i, 4);// i=44
  312. $i += 4;
  313. // 第一组样点速度
  314. $first_location_speed = self::stitching($body, $i, 1);// i=48
  315. $i += 1;
  316. // 第一组样点ACC状态、方向、卫星数
  317. $first_location_acc = self::stitching($body, $i, 1);// i=49
  318. $i += 1;
  319. // 第一个点
  320. $first_location = [
  321. 'location_time' => $this->decodeTime($first_location_time),
  322. 'location_latitude' => hexdec($first_location_latitude) / 1000000.0,
  323. 'location_longitude' => hexdec($first_location_longitude) / 1000000.0,
  324. 'location_speed' => self::handleLocationSpeed($first_location_speed),
  325. 'location_acc' => self::handleLocationACC($first_location_acc),
  326. ];
  327. // 第x个点的速度 1<= x <= hexdec($message_num)
  328. // 假设为第三个点的
  329. $locations[0] = $first_location;
  330. if ($message_num > 2) {
  331. $ylocation = [
  332. 'location_time' => hexdec($first_location_time),
  333. 'location_latitude' => hexdec($first_location_latitude),
  334. 'location_longitude' => hexdec($first_location_longitude),
  335. 'location_speed' => self::handleLocationSpeed($first_location_speed),
  336. 'location_acc' => self::handleLocationACC($first_location_acc),
  337. ];
  338. for ($x = 1; $x < ($message_num - 1); $x++) {
  339. $x_location_speed = self::stitching($body, $i, 1);// i=58
  340. $i += 1;
  341. $x_location_acc = self::stitching($body, $i, 1);// i=59
  342. $i += 1;
  343. // n 与 n-1的时间差值
  344. $x_location_time = self::stitching($body, $i, 2);// $i =60
  345. $i += 2;
  346. // n与n-1 的维度差值
  347. $x_location_latitude = self::stitching($body, $i, 2);// 62
  348. // self::log(hexdec($x_location_latitude),'log',2);
  349. $i += 2;
  350. // n与n-1的经度差值
  351. $x_location_longitude = self::stitching($body, $i, 2);// 64
  352. // self::log(hexdec($x_location_longitude),'log',2);
  353. $i += 2;
  354. // self::log('$i:' . $i . PHP_EOL,'i',2);
  355. $ylocation = [
  356. 'location_time' => $ylocation['location_time'] + hexdec($x_location_time),
  357. 'location_latitude' => $ylocation['location_latitude'] + self::handleI2($x_location_latitude),
  358. 'location_longitude' => $ylocation['location_longitude'] + self::handleI2($x_location_longitude),
  359. ];
  360. $locations[$x] = [
  361. 'location_time' => date('Y-m-d H:i:s', $ylocation['location_time'] + hexdec($x_location_time)),
  362. 'location_latitude' => ($ylocation['location_latitude'] + self::handleI2($x_location_latitude)) / 1000000.0,
  363. 'location_longitude' => ($ylocation['location_longitude'] + self::handleI2($x_location_longitude)) / 1000000.0,
  364. 'location_speed' => self::handleLocationSpeed($x_location_speed),
  365. 'location_acc' => self::handleLocationACC($x_location_acc),
  366. ];
  367. }
  368. }
  369. // 最后一个点
  370. $last_location = [];
  371. if ($message_num > 1) {
  372. //最后一组样点与第一个样点时间差值
  373. $last_location_time = self::stitching($body, $i, 2);// i=50
  374. $i += 2;
  375. // 最后一组样点与第一个样点维度差值
  376. $last_location_latitude = self::stitching($body, $i, 2);// i=52
  377. $i += 2;
  378. // 最后一组样点与第一个样点经度差值
  379. $last_location_longitude = self::stitching($body, $i, 2);// i=54
  380. $i += 2;
  381. // 最后一组样点速度
  382. $last_location_speed = self::stitching($body, $i, 1);// i=56
  383. $i += 1;
  384. // 最后一组样点ACC
  385. $last_location_acc = self::stitching($body, $i, 1);// i=57
  386. $i += 1;
  387. $last_location = [
  388. 'location_time' => $this->decodeTime(dechex(hexdec($first_location_time) + self::handleI2($last_location_time))),
  389. 'location_latitude' => (hexdec($first_location_latitude) + self::handleI2($last_location_latitude)) / 1000000.0,
  390. 'location_longitude' => (hexdec($first_location_longitude) + self::handleI2($last_location_longitude)) / 1000000.0,
  391. 'location_speed' => self::handleLocationSpeed($last_location_speed),
  392. 'location_acc' => self::handleLocationACC($last_location_acc),
  393. ];
  394. $locations[] = $last_location;
  395. }
  396. self::log(count($locations), 'ZU');
  397. return array_merge($top34Data, [
  398. 'location_type' => self::handleLocationType($location_type),
  399. 'message_num' => hexdec($message_num),
  400. 'first_location' => $first_location,
  401. 'last_location' => $last_location,
  402. 'locations' => $locations
  403. ]);
  404. }
  405. /**
  406. * 处理设备序列号
  407. * @param $no
  408. * @return bool|string
  409. * User: Mead
  410. */
  411. private
  412. static function box_no($no)
  413. {
  414. return substr($no, 0, 9);
  415. }
  416. /**
  417. * 解析点类型
  418. *
  419. * @param $location_type
  420. * @return array|mixed
  421. * @author Fx
  422. *
  423. */
  424. private static function handleLocationType($location_type)
  425. {
  426. $data = self::handleU1($location_type);
  427. return [
  428. 'location_type' => $data[5],// 1.静态位置标示;0.正常的轨迹点
  429. 'is_speeding' => $data[4] //超速指示,当该数据包持续6个点速度超速时,该位为1
  430. ];
  431. }
  432. /**
  433. * 解析速度
  434. *
  435. * @param $location_speed
  436. * @return array
  437. * @author Fx
  438. *
  439. */
  440. private static function handleLocationSpeed($location_speed)
  441. {
  442. // self::dd('Speed==>');
  443. // self::dd($location_speed);
  444. $data = self::handleU1($location_speed);
  445. // self::dd($data);
  446. $is_speeding = $data[7];
  447. $speed = implode('', array_slice($data, 0, 7));
  448. return [
  449. 'is_speeding' => $is_speeding, //1:超速 0:否
  450. 'speed' => bindec($speed) * 2// 量化单位2km/h,范围 0~127
  451. ];
  452. }
  453. private static function handleLocationACC($locationACC)
  454. {
  455. // self::dd('ACC==>');
  456. // self::dd($locationACC);
  457. $data = self::handleU1($locationACC);
  458. // self::dd($data);
  459. // 卫星数
  460. $satellites = bindec(implode('', array_slice($data, 0, 4)));
  461. // 方向
  462. $directions = [
  463. '000' => '正北',
  464. '001' => '东北',
  465. '010' => '正东',
  466. '011' => '东南',
  467. '100' => '正南',
  468. '101' => '西南',
  469. '110' => '正西',
  470. '111' => '西北',
  471. ];
  472. $direction = implode('', array_slice($data, 4, 3));
  473. // ACC状态 1:ACC接通;0:ACC断开
  474. $acc_status = $data[7];
  475. return [
  476. 'satellites' => $satellites,
  477. 'direction' => $directions[$direction] ?? '',
  478. 'acc_status' => $acc_status,
  479. ];
  480. }
  481. /**
  482. * 判断是否在骑行区
  483. * @param $lat
  484. * @param $lng
  485. * @param $box_no
  486. * User: Mead
  487. */
  488. private function isOutArea($lat, $lng, $area_id = false, $box_no = false)
  489. {
  490. $location = [
  491. 'latitude' => $lat,
  492. 'longitude' => $lng,
  493. ];
  494. if (!$area_id) $area_id = $this->byBoxNoGetAreaId($box_no);
  495. $area = $this->byAreaIdGetArea($area_id);
  496. $fences = $area['area_fence'];
  497. $centre = $area['area_centre'];
  498. $radius = $area['area_radius'];
  499. $area_fushe_fence = $area['area_fushe_fence'];
  500. // 判断是否在骑行区域
  501. $ConvertHandler = (new ConvertHandler());
  502. $is_out_area = $ConvertHandler->is_point_in_polygon($location, $fences);
  503. // 判断是否骑出辐射范围
  504. $is_out_area_nearby = true;
  505. if (!$is_out_area) {
  506. $is_out_area_nearby = $ConvertHandler->is_point_in_polygon($location, $area_fushe_fence);
  507. }
  508. return [
  509. 'is_out_area' => $is_out_area,
  510. 'is_out_area_nearby' => $is_out_area_nearby
  511. ];
  512. }
  513. }