tarfin-labs / laravel-spatial
Laravel 地理空间数据类型和函数处理的包。
Requires
- php: ^8.0|^8.1|^8.2|^8.3
- illuminate/support: ^8.0|^9.0|^10.0|^11.0
Requires (Dev)
- doctrine/dbal: ^3.3
- orchestra/testbench: ^6.0|^7.0|^8.0|^9.0
- phpunit/phpunit: ^9.0
- dev-main
- v2.0.0
- v1.7.0
- v1.6.1
- v1.6.0
- v1.5.0
- v1.4.1
- v1.4.0
- v1.3.0
- v1.2.0
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.0
- dev-update-php-version
- dev-dev
- dev-wkt-options-and-bulk-operations
- dev-readme-explanation-for-default-point-value
- dev-laravel-10-support
- dev-doctrine-type-error-fix
- dev-filtering-zero-points
- dev-optimizations
- dev-laravel-9-support
- dev-linestring-spatial-data-type
- dev-master
This package is auto-updated.
Last update: 2024-09-25 13:38:40 UTC
README
这是一个用于处理地理空间数据类型和函数的 Laravel 包。
它仅支持 MySQL 地理空间数据类型和函数,其他 RDBMS 将在路线图上。
Laravel 兼容性
支持的数据类型
点
可用的作用域
withinDistanceTo($column, $coordinates, $distance)
selectDistanceTo($column, $coordinates)
orderByDistanceTo($column, $coordinates, 'asc')
安装
您可以通过 composer 安装此包。
composer require tarfin-labs/laravel-spatial
用法
使用迁移文件生成新的模型
php artisan make:model Address --migration
1- 迁移
基于 Laravel 版本的代码差异
项目中的某些代码片段在 Laravel 11 版本之前和之后有所不同。以下是指定这些差异的步骤
对于 Laravel 8、9 和 10 版本
要添加空间数据字段,您需要从 TarfinLabs\LaravelSpatial\Migrations\SpatialMigration
扩展迁移。
这是一个简单的抽象类,在构造函数中添加了 point
空间数据类型到 Doctrine 映射类型。
use TarfinLabs\LaravelSpatial\Migrations\SpatialMigration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends SpatialMigration { public function up(): void { Schema::create('addresses', function (Blueprint $table) { $table->point('location'); }) } }
上述迁移创建了一个具有 location
空间列的 addresses
表。
没有 SRID 属性的空间列不受 SRID 限制,接受任何 SRID 的值。然而,优化器无法在这些列上使用 SPATIAL 索引,直到将列定义修改为包含 SRID 属性,这可能需要首先修改列内容,以确保所有值具有相同的 SRID。
因此,您应该在迁移中提供 SRID 属性以使用空间索引,并且索引列必须为 NOT NULL
Schema::create('addresses', function (Blueprint $table) { $table->point(column: 'location', srid: 4326); $table->spatialIndex('location'); })
对于 Laravel 11 及以上版本
从 Laravel 11 开始,迁移创建如下
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { Schema::create('addresses', function (Blueprint $table) { $table->geography('location', 'point'); }) } }
从 Laravel 11 开始,删除了 point、lineString、polygon、geometryCollection、multiPoint、multiLineString 和 multiPolygon 方法。因此,我们更新为使用 geography 方法。该 geography
方法将默认 SRID 值设置为 4326。
向现有表添加带索引的新位置列的问题
当在 Laravel 中添加带索引的新位置列时,如果存在现有数据,可能会很麻烦。一个常见的错误是尝试使用 ->default(new Point(0, 0, 4326))
为新列设置默认值。然而,POINT
列不能有默认值,这可能导致在尝试向列添加索引时出现问题,因为索引列不能为可空。
要解决这个问题,建议执行以下两步迁移
对于 Laravel 8、9 和 10 版本
public function up() { // Add the new location column as nullable Schema::table('table', function (Blueprint $table) { $table->point('location')->nullable(); }); // In the second go, set 0,0 values, make the column not null and finally add the spatial index Schema::table('table', function (Blueprint $table) { DB::statement("UPDATE `table` SET `location` = POINT(0,0);"); DB::statement("ALTER TABLE `table` CHANGE `location` `location` POINT NOT NULL;"); $table->spatialIndex('location'); }); }
对于 Laravel 11 及以上版本
public function up() { // Add the new location column as nullable Schema::table('table', function (Blueprint $table) { $table->geography('location', 'point')->nullable(); }); // In the second go, set 0,0 values, make the column not null and finally add the spatial index Schema::table('table', function (Blueprint $table) { DB::statement("UPDATE `addresses` SET `location` = ST_GeomFromText('POINT(0 0)', 4326);"); DB::statement("ALTER TABLE `table` CHANGE `location` `location` POINT NOT NULL;"); $table->spatialIndex('location'); }); }
2- 模型
在模型中填充 $fillable
、$casts
数组
use Illuminate\Database\Eloquent\Model; use TarfinLabs\LaravelSpatial\Casts\LocationCast; use TarfinLabs\LaravelSpatial\Traits\HasSpatial; class Address extends Model { use HasSpatial; protected $fillable = [ 'id', 'name', 'address', 'location', ]; protected array $casts = [ 'location' => LocationCast::class ]; }
3- 空间数据类型
点
Point
表示位置的坐标,并包含 latitude
、longitude
和 srid
属性。
此时,理解 SRID 非常重要。每个空间实例都有一个空间参考标识符 (SRID)。SRID 与基于特定椭球体(用于平面地球测绘或球面地球测绘)的空间参考系统相对应。空间列可以包含具有不同 SRID 的对象。
有关 SRID 的详细信息,请参阅以下链接: https://en.wikipedia.org/wiki/Spatial_reference_system
latitude
、longitude
参数的默认值是0.0
。srid
参数的默认值是0
。
use TarfinLabs\LaravelSpatial\Types\Point; $location = new Point(lat: 28.123456, lng: 39.123456, srid: 4326); $location->getLat(); // 28.123456 $location->getLng(); // 39.123456 $location->getSrid(); // 4326
您可以通过laravel-spatial
配置文件覆盖默认的SRID。为此,您应该使用vendor:publish
artisan命令发布配置文件
php artisan vendor:publish --provider="TarfinLabs\LaravelSpatial\LaravelSpatialServiceProvider"
之后,您可以在config/laravel-spatial.php
中更改default_srid
的值
return [ 'default_srid' => 4326, ];
配置WKT选项
默认情况下,此包使用经度 纬度
的顺序来表示空间函数中使用的WKT格式的坐标值。对于某些版本的MySQL,如果不将axis-order
选项明确设置为long-lat
,则会将坐标对解释为纬-经。
然而,MariaDB默认将WKT值读取为long-lat
,其空间函数如ST_GeomFromText
和ST_DISTANCE
不接受与MySQL对应函数类似的options
参数。这意味着在使用MariaDB时使用此包会导致语法错误或访问违规:1582 在调用本地函数'ST_GeomFromText'时参数数量不正确
异常。
为了解决这个问题,我们在配置文件中添加了with_wkt_options
参数,可以用来覆盖默认选项。此属性可以设置为false
以完全删除选项参数,这可以修复使用MariaDB时的错误。
return [ 'with_wkt_options' => true, ];
批量操作
为了使用Laravel中的upsert()
方法在一个查询中插入或更新多个具有空间数据的行,该包需要一个解决方案来避免错误Object of class TarfinLabs\LaravelSpatial\Types\Point could not be converted to string
。
解决方案是使用toGeomFromText()
方法将Point
对象转换为WKT字符串,然后使用DB::raw()
创建原始查询字符串。
以下是如何在代码中使用此解决方案的示例
use TarfinLabs\LaravelSpatial\Types\Point; $points = [ ['external_id' => 5, 'location' => DB::raw((new Point(lat: 40.73, lng: -73.93))->toGeomFromText())], ['external_id' => 7, 'location' => DB::raw((new Point(lat: -37.81, lng: 144.96))->toGeomFromText())], ]; Property::upsert($points, ['external_id'], ['location']);
4- 范围
withinDistanceTo()
您可以使用withinDistanceTo()
范围根据给定的距离筛选位置
要筛选给定坐标10公里范围内的地址
use TarfinLabs\LaravelSpatial\Types\Point; use App\Models\Address; Address::query() ->withinDistanceTo('location', new Point(lat: 25.45634, lng: 35.54331), 10000) ->get();
selectDistanceTo()
您可以使用selectDistanceTo()
范围通过两点之间的距离。距离将以米为单位
use TarfinLabs\LaravelSpatial\Types\Point; use App\Models\Address; Address::query() ->selectDistanceTo('location', new Point(lat: 25.45634, lng: 35.54331)) ->get();
orderByDistanceTo()
您可以根据给定的坐标对模型进行排序
use TarfinLabs\LaravelSpatial\Types\Point; use App\Models\Address; // ASC Address::query() ->orderByDistanceTo('location', new Point(lat: 25.45634, lng: 35.54331)) ->get(); // DESC Address::query() ->orderByDistanceTo('location', new Point(lat: 25.45634, lng: 35.54331), 'desc') ->get();
获取位置的纬度和经度
use App\Models\Address; $address = Address::find(1); $address->location; // TarfinLabs\LaravelSpatial\Types\Point $address->location->getLat(); $address->location->getLng();
创建一个新的具有位置的地址
use App\Models\Address; Address::create([ 'name' => 'Bag End', 'address' => '1 Bagshot Row, Hobbiton, Shire', 'location' => new Point(lat: 25.45634, lng: 35.54331), ]);
在资源中的使用
要获取资源中位置转换字段的数组表示形式,您可以返回parent::toArray($request)
。
如果您需要从资源返回自定义数组,则可以使用Point
对象的toArray()
方法。
class LocationResource extends JsonResource { public function toArray($request) { return [ 'location' => $this->location->toArray(), ]; } }
无论如何,您都会得到以下输出作为位置转换字段
{ "lat": 25.45634, "lng": 35.54331, "srid": 4326 }
测试
composer test
变更日志
请参阅变更日志以获取有关最近更改的更多信息。
贡献
请参阅贡献指南以获取详细信息。
安全性
如果您发现任何与安全相关的问题,请通过development@tarfin.com发送电子邮件,而不是使用问题跟踪器。
鸣谢
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。