clickbar / laravel-magellan
此包为在 Laravel 中使用 postgis 扩展提供功能。
Requires
- php: ^8.1
- illuminate/console: ^9.0|^10.0|^11.0
- illuminate/contracts: ^9.28|^10.0|^11.0
- illuminate/database: ^9.0|^10.0|^11.0
- illuminate/support: ^9.0|^10.0|^11.0
- spatie/invade: ^2.0
- spatie/laravel-package-tools: ^1.14.0
Requires (Dev)
- doctrine/dbal: ^3.5
- larastan/larastan: ^2.6
- laravel/pint: ^1.2.1
- nunomaduro/collision: ^6.0|^v8.1
- orchestra/testbench: ^7.15|^8.0|^9.0
- pestphp/pest: ^1.22|^2.34
- pestphp/pest-plugin-laravel: ^1.1|^2.3
- phpstan/extension-installer: ^1.2
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.2
- phpunit/phpunit: ^9.5|^10.5
- spatie/laravel-ray: ^1.26
This package is auto-updated.
Last update: 2024-09-17 15:03:56 UTC
README
一个现代化的 Laravel PostGIS 工具箱
简介
每一位水手都需要一艘美丽的船来航行七大洋 ⛵️
此包将帮助您在 Laravel 中访问与 PostGIS 相关的功能。它受到了 mstaack/laravel-postgis 的强烈启发,但自从那时起已经发展得更加完善。除了一些轻微的变化外,您应该很快就能熟悉 Magellan。
Magellan 内置了桨,并提供 GeoJson、WKB & WKT 的解析器/生成器。您可以通过使用我们的构建器函数,轻松在迁移中使用所有 PostGIS 数据类型,并避免使用原始 SQL 访问 PostGIS 函数。
此外,laravel-magellan
为 Schema、Query Builder 和 Postgres Grammar 提供了扩展,以便轻松访问像 ST_EXTENT
这样的 PostGIS 数据库函数。它所有这些操作都不会破坏与其他包的兼容性,例如需要扩展语法和连接的 tpetry/laravel-postgresql-enhanced。
要求
Magellan 支持 Laravel 项目,这些项目满足以下要求
- Laravel
^9.28
或^10.0
- PHP
^8.1
安装
您可以通过 composer 安装此包
composer require clickbar/laravel-magellan
您可以使用以下命令发布和运行迁移
php artisan vendor:publish --tag="magellan-migrations"
php artisan migrate
您可以使用以下命令发布配置文件
php artisan vendor:publish --tag="magellan-config"
您可以在以下位置找到已发布的配置文件的内容: config/magellan.php
包含内容
- 迁移 Schema 蓝图
- 几何数据类
- WKT 生成器 & 解析器
- WKB 生成器 & 解析器
- GeoJson 生成器 & 解析器
- Eloquent 模型特性
- 自动将 PostGIS 特性添加到模型中的命令
- 使用不同投影自动转换插入
- GeoJson 请求验证规则
- 为表单请求转换几何形状
- 几乎将所有 PostGIS 函数作为类型化函数暴露,可用于 select、where、orderBy、groupBy、having、from
- 几何和 BBox 转换类
- 使用返回几何或 bbox 的函数时自动转换
- 空几何形状支持
- 自定义更新 Builder 方法以提高转换安全性
- 自动 PostGIS 函数文档生成器
- BBox 支持在 $postgisColumns & 特性中(目前仅支持转换)
- 自定义几何工厂 & 模型
- 更多测试
- ...
开始之前
我们强烈建议使用 barryvdh 的 laravel-ide-helper,以便能够在 IDEs 的自动完成中查看所有包含的内容。
使用 PostGIS 列创建表
Laravel-magellan 扩展了默认的 Schema Blueprint 以包含所有 PostGIS 函数。由于 Laravel 已经引入了基本的几何支持,所有方法都以前缀 magellan
开头。例如:
$table->magellanPoint('location', 4326);
准备模型
为了正确地将所有内容集成到模型中,您需要执行以下 2 个步骤
- 将
HasPostgisColumns
特性添加到您的模型中 - 将
$postgisColumns
数组添加到模型中
protected array $postgisColumns = [ 'location' => [ 'type' => 'geometry', 'srid' => 4326, ], ];
以下命令可以自动化这两个步骤:
php artisan magellan:update-postgis-columns
该命令会自动扫描数据库并添加特性和数组。
使用几何数据类
我们包括以下常见几何形状的数据类:
- 点
- 线字符串
- 多边形
- 多点
- 多线字符串
- 多多边形
- 几何集合
要手动创建几何对象,请使用相应的 <GeometryClass>::make
方法。例如:
$point = Point::make(51.087, 8.76);
您会注意到,对于点类,有3种不同的带有不同参数的 make 方法
make(...)
makeGeodetic(...)
makeEmpty(...)
让我们更详细地看看前两种
这是默认的工厂方法,可用于填充所有可能的值。该方法被认为是“普通”方式。当您使用非 lng/lat 投影(例如,不是 WGS84:srid=4326)时,应考虑使用此方法。
function make(float $x, float $y, ?float $z = null, ?float $m = null, ?int $srid = null): self
大多数常见的网络使用案例都使用 WGS84 投影。因此,大多数情况下,使用的术语将是纬度、经度和高度,而不是 x、y 和 z。为了提供更多便利,我们包括了一个接受这些术语并自动将 srid 设置为默认大地测量 srid 的工厂方法,该 srid 可以在配置文件中设置。
function makeGeodetic(float $latitude, float $longitude, ?float $altitude = null, ?float $m = null): self
当使用使用大地测量投影的点类时,您可以通过具有适当名称的获取器和设置器访问纬度、经度和高度
function getLatitude(): float
function setLatitude(float $latitude): void
function getLongitude(): float
function setLongitude(float $longitude): void
function getAltitude(): ?float
function setAltitude(float $altitude): void
如果尝试在没有在 geodetic_srids 配置中列出 srid 的点上使用此函数,将抛出异常。请改用默认的 x、y、z、m 获取器和设置器。
生成器和解析器
我们目前提供以下格式的解析器和生成器:
- EWKB
- EWKT
- GeoJson
这些也用于将我们的数据类格式化为字符串,将数据库返回的值(以 EWKB 格式)转换为字符串,并将数据输出到前端,例如 GeoJson。
注意 在以下内容中,我们将 EWKB & WBK 或 EWKT & WKT 互换使用,尽管我们总是使用每个的扩展版本。
配置文件允许您自定义您希望使用的表示方式,例如,当对数据类进行 JSON 序列化时,GeoJson 是默认的。
$point = Point::makeGeodetic(51.087, 8.76); json_encode($point); // returns GeoJson // "{"type":"Point","coordinates":[8.76,51.087]}"
您始终可以使用每个解析器/生成器的实例自行进行解析/生成。
虽然生成器必须在需要时创建,但解析器已在应用容器中作为单例实例化,您可以使用如下方式使用它们:
$parser = app(WKTParser::class); $point = $parser->parse('SRID=4326;POINT (2, 2)'); $generator = new WKBGenerator(); $generator->generate($point); // "0101000020E610000000000000000000400000000000000040"
在此示例中,我们获取 WKTParser
的实例并将字符串转换为我们的数据类之一。$point
是一个有效的 Point
实例,然后我们可以使用其他生成器,例如 WKBGenerator
,将 $point
输出为十六进制 WKB 格式。
请求验证和转换
当一个表单请求包含 Geojson 格式的几何形状时,您可以使用 GeometryGeojsonRule
进行验证。您甚至可以通过传递包含类的数组来限制允许的几何形状类型。
为了正确地继续处理接收到的几何形状,您可以使用 TransformsGeojsonGeometry
特性来自动将 geojson 转换为正确的几何对象。因此,请返回 geometries(): array
函数中的键。
注意 目前我们只支持简单的字段转换。数组和通配符表示支持将随后到来。
class StorePortRequest extends FormRequest { use TransformsGeojsonGeometry; public function rules(): array { return [ 'name' => ['required', 'string'], 'country' => ['required', 'string'], 'location' => ['required', new GeometryGeojsonRule([Point::class])], ]; } public function geometries(): array { return ['location']; } }
与数据库交互
示例设置
出于演示目的,我们考虑以下虚构场景:
我们是一名帆船爱好者,拥有一艘可爱的帆船和世界各地的几个港口的数据库。
对于每个端口,我们存储名称、国家和位置。
以下是用于创建端口表的迁移操作
Schema::create('ports', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('country'); $table->magellanPoint('location'); $table->timestamps(); });
以及模型实现
class Port extends Model { use HasFactory; use HasPostgisColumns; protected $guarded = []; protected array $postgisColumns = [ 'location' => [ 'type' => 'geometry', 'srid' => 4326, ], ]; }
插入/更新
只要在受影响模型的 $postgisColumns
中指定,Magellan 几何对象就可以直接插入。在我们的例子中,我们可以这样插入一个新的端口
Port::create([ 'name' => 'Magellan Home Port', 'country' => 'Germany', 'location' => Point::makeGeodetic(49.87108851299202, 8.625026485851762), ]);
当您想更新一个几何对象时,您可以将新位置分配给模型并调用 save()
,或者在使用查询构建器的 update()
方法上使用
$port->location = Point::makeGeodetic(55, 11); $port->save(); // -- or -- Port::where('name', 'Magellan Home Port') ->update(['location' => Point::makeGeodetic(55, 11)]);
具有不同 SRID 的插入/更新
从外部系统获取几何对象时,您可能收到与数据库中不同的投影。考虑我们想要插入或更新一个具有不同 SRID 的几何对象
Port::create([ 'name' => 'Magellan Home Port', 'country' => 'Germany', 'location' => Point::make(473054.9891044726, 5524365.310057224, srid: 25832), ]); // -- or -- $port = Port::find(1); $port->location = Point::make(473054.9891044726, 5524365.310057224, srid: 25832); $port->save();
由于我们的端口表使用 SRID=4326 的点,Magellan 将引发错误
SRID 不匹配:数据库具有 SRID 4326,几何对象具有 SRID 25832。考虑启用
magellan.eloquent.transform_to_database_projection
以应用自动转换
我们包含了一个自动转换选项,它可以直接为您应用 ST_Transform(geometry, databaseSRID)
。
注意
此选项仅当直接在 eloquent 模型上插入/更新时应用。
此选项不应用于地理列。
选择
当从使用 HasPostgisColumns
特性的模型中选择数据时,所有属性都将直接解析到内部数据类中
$port = Port::first(); dd($port->location);
Clickbar\Magellan\Data\Geometries\Point {#1732 #srid: 4326 #dimension: Clickbar\Magellan\Data\Geometries\Dimension {#740 +name: "DIMENSION_2D" +value: "2D" } #x: 8.6250264858452 #y: 49.87108851299 #z: null #m: null }
可能存在您还想要使用 box2d 或 box3d 作为列类型的情况。目前,我们不支持在 $postgisColumns
中的盒子。请使用 BBoxCast
。
在查询中使用 PostGIS 函数
laravel-magallan 的一大特点是其广泛的查询构建功能。为了提供无缝且易于使用 PostGIS 函数,我们包括了一组广泛的应用通常以 ST-前缀的函数,可以直接与 Laravel 的查询构建器一起使用。
每次您想要在查询构建器上使用 PostGIS 函数时,您都必须使用我们的构建器方法之一。所有这些方法都以 st
为前缀。
我们目前提供以下功能
- stSelect
- stWhere
- stOrWhere
- stOrderBy
- stGroupBy
- stHaving
- stFrom
注意
使用 MagellanExpression 并返回布尔值的 stWhere 总是需要在后面加上 true 或 false。这是 Laravel 使用 ->where() 时的默认行为,但由于 PHP 支持诸如 if($boolean) 之类的功能,而无需显式 $boolean == true 条件,true/false 容易被遗忘,从而导致查询而不是布尔查询。
->stWhere(ST::contains('location', 'polygon'), true)
这些构建器方法都期望接收一个 MagellanExpression。
MagellanExpression 是 PostGIS 中 ST
-前缀函数的包装器。当与 Magellan 一起航行时,您永远不需要自己编写 ST_xxx
的原始 SQL。因此,我们包含了一些浆。
大多数 ST
-前缀的函数可以通过 ST
类的静态函数访问。但不要多说,让我们开始航行(一些示例)
注意:必要的类可以按以下方式导入
use Clickbar\Magellan\Data\Geometries\Point; use Clickbar\Magellan\Database\PostgisFunctions\ST;
假设我们有我们船只的当前位置,并想查询所有港口及其距离
$currentShipPosition = Point::makeGeodetic(50.107471773560114, 8.679861151457937); $portsWithDistance = Port::select() ->stSelect(ST::distanceSphere($currentShipPosition, 'location'), 'distance_to_ship') ->get();
由于我们无法航行整个世界,让我们将距离限制在最大 50,000 米
$currentShipPosition = Point::makeGeodetic(50.107471773560114, 8.679861151457937); $portsWithDistance = Port::select() ->stSelect(ST::distanceSphere($currentShipPosition, 'location'), 'distance_to_ship') ->stWhere(ST::distanceSphere($currentShipPosition, 'location'), '<=', 50000) ->get();
现在让我们根据距离对他们进行排序
$currentShipPosition = Point::makeGeodetic(50.107471773560114, 8.679861151457937); $portsWithDistance = Port::select() ->stSelect(ST::distanceSphere($currentShipPosition, 'location'), as: 'distance_to_ship') ->stWhere(ST::distanceSphere($currentShipPosition, 'location'), '<=', 50000) ->stOrderBy(ST::distanceSphere($currentShipPosition, 'location')) ->get();
如您所见,使用 st
-Builder 函数与使用默认 Laravel 函数一样简单。但更复杂的查询怎么办?关于按国家分组的所有港口的凸包,包括凸包的面积怎么办?没问题
$hullsWithArea = Port::select('country') ->stSelect(ST::convexHull(ST::collect('location')), 'hull') ->stSelect(ST::area(ST::convexHull(ST::collect('location')))) ->groupBy('country') ->get();
自动将 bbox 或几何对象转换为类型
在上一节中,我们使用了一些PostGIS函数。在第一个示例中,返回类型只包含标量值。但在更复杂的示例中,我们收到了一个几何形状作为返回值。
由于“hull”不在我们的$postgisColumns
数组中,我们可能会故意将查询添加为类型转换
$hullWithArea = Port::select('country') ->stSelect(ST::convexHull(ST::collect('location')), 'hull') ->stSelect(ST::area(ST::convexHull(ST::collect('location')))) ->groupBy('country') ->withCasts(['hull' => GeometryWKBCast::class]) /* <======= */ ->first();
但这是不必要的!
Magellan会自动为所有返回几何形状、box2d或box3d的函数添加类型转换。
测试
composer test
变更日志
有关最近更改的详细信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献指南。
安全漏洞
有关详细信息,请参阅安全漏洞。
谢谢
鸣谢
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。