Laravel+Swoole+PHP-ml 实现根据经纬度返回对应城市
今天不是很忙,写个小东西分享给大家。
https://github.com/Aquarmini/laravel-phpml...
首先我们新建个项目
composer create laravel/laravel
composer require swooletw/laravel-swoole
composer require php-ai/php-ml
然后我们把配置生成一下,选择 SwooleTW\Http\LaravelServiceProvider
php artisan vendor:publish
接下来生成一下模型
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* @property $oid
* @property $area_name
* @property $level
* @property $parent_oid
* @property $province_oid
* @property $city_oid
* @property $county_oid
* @property $area_code
* @property $simple_name
* @property $lon
* @property $lat
* @property $zip_code
* @property $whole_name
* @property $whole_oid
* @property $pre_pin_yin
* @property $pin_yin
* @property $simple_py
* @property $remark
* @property $version
*/
class PoiDistricts extends Model
{
/**
* The primary key for the model.
*
* @var string
*/
public $primaryKey = 'oid';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'poi_districts';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['oid', 'area_name', 'level', 'parent_oid', 'province_oid', 'city_oid', 'county_oid', 'area_code', 'simple_name', 'lon', 'lat', 'zip_code', 'whole_name', 'whole_oid', 'pre_pin_yin', 'pin_yin', 'simple_py', 'remark', 'version'];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = ['oid' => 'integer', 'level' => 'integer', 'parent_oid' => 'integer', 'province_oid' => 'integer', 'city_oid' => 'integer', 'county_oid' => 'integer', 'lon' => 'float', 'lat' => 'float', 'version' => 'float'];
}
接下来我们写一个生成训练用数据的脚本。
<?php
namespace App\Console\Commands;
use App\PoiDistricts;
use Illuminate\Console\Command;
use Swoole\Runtime;
class PoiDistrictCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ml:districts';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate samples and labels.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
Runtime::enableCoroutine(false);
$arrays = PoiDistricts::query()->where('level', 5)->get(['parent_oid', 'lon', 'lat']);
$input = [];
$result = [];
foreach ($arrays as $item) {
if (is_numeric($item['lon']) && is_numeric($item['lat'])) {
$input[] = [
$item['lon'],
$item['lat']
];
$result[] = $item['parent_oid'];
}
}
file_put_contents(base_path('storage/samples'), serialize($input));
file_put_contents(base_path('storage/labels'), serialize($result));
}
}
接下来生成我们的训练数据
php artisan ml:districts
这时,就可以在storage目录下看到我们的训练数据 samples
和 labels
了。
继续我们的开发,后面就是编写路由和控制器
<?php
# Route::get('/', 'IndexController@index');
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class IndexController extends Controller
{
public function index(Request $request)
{
$lon = $request->input('lon');
$lat = $request->input('lat');
// TODO: 查询对应数据
return [];
}
}
接下来完善我们用于训练数据的类
<?php
namespace App;
use Illuminate\Container\Container;
use Phpml\Classification\KNearestNeighbors;
class PoiDistrictService
{
/**
* @var KNearestNeighbors
*/
protected $classifier;
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
$samples = unserialize(file_get_contents(base_path('storage/samples')));
$labels = unserialize(file_get_contents(base_path('storage/labels')));
$this->classifier = new KNearestNeighbors();
$this->classifier->train($samples, $labels);
}
/**
* @return KNearestNeighbors
*/
public function getClassifier(): KNearestNeighbors
{
return $this->classifier;
}
}
然后在AppServiceProvider中注册一下我们的训练库
<?php
namespace App\Providers;
use App\PoiDistrictService;
use Illuminate\Container\Container;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
Container::getInstance()->singleton(PoiDistrictService::class, function ($app) {
return new PoiDistrictService($app);
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
}
}
接下来就简单了,重写我们之前的控制器。
<?php
namespace App\Http\Controllers;
use App\PoiDistricts;
use App\PoiDistrictService;
use Illuminate\Http\Request;
class IndexController extends Controller
{
public function index(Request $request)
{
$lon = $request->input('lon');
$lat = $request->input('lat');
// 查询对应数据
$client = app()->get(PoiDistrictService::class)->getClassifier();
$id = $client->predict([$lon, $lat]);
$model = PoiDistricts::query()->where('oid', $id)->first();
return $model->toArray();
}
}
最后就是测试一下了
php artisan swoole:http start
curl http://127.0.0.1:1215/?lon=119.2716979980&lat=36.7608912496
{
"oid": 1782,
"area_name": "奎文区",
"level": 4,
"parent_oid": 177,
"province_oid": 6,
"city_oid": 177,
"county_oid": 1782,
"area_code": "0536",
"simple_name": "奎文",
"lon": 119.12532,
"lat": 36.70723,
"zip_code": "261000",
"whole_name": "中国,山东省,潍坊市,奎文区",
"whole_oid": "1,6,177,1782",
"pre_pin_yin": "K",
"pin_yin": "Kuiwen",
"simple_py": "KW",
"remark": "",
"version": 1
}
响应时间100ms左右,其实也还不错。
并发场景下还是和专门为swoole开发的框架有一定差距。
不过差距也不太大咯,反正也没人会在PHP里搞这个吧。。
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
很有意思,感谢分享
有点意思~顺手赞一个
这个有意思了.
想到我们之前是通过前端 js 调取腾讯地图实现的.
@Ali 是的,不过PHP计算性能不大好。也只能玩玩了。。
厉害了