grimzy/laravel-mysql-spatial

Laravel 的 MySQL 空间数据类型扩展。

5.0.0 2020-10-17 07:28 UTC

README

Build Status Code Climate Code Climate Packagist Packagist StyleCI license

Laravel 包,用于轻松处理 MySQL 空间数据类型MySQL 空间函数

请检查您 MySQL 版本的文档。MySQL 的空间数据扩展自 MySQL 5.5 开始支持,但许多空间函数在 5.6 和 5.7 中发生了变化。

版本

  • 1.x.x: MySQL 5.6(也支持 MySQL 5.5,但不是所有空间分析函数)
  • 2.x.x: MySQL 5.7 和 8.0(Laravel 版本 < 8.0)
  • 3.x.x: MySQL 8.0,支持 SRID(Laravel 版本 < 8.0)
  • 4.x.x: MySQL 8.0,支持 SRID(Laravel 8+)[当前分支]
  • 5.x.x: MySQL 5.7 和 8.0(Laravel 8+)

此包也支持 MariaDB。请参阅 MySQL/MariaDB 空间支持矩阵 了解兼容性。

安装

使用 composer 添加包

$ composer require grimzy/laravel-mysql-spatial:^4.0

# or for Laravel version < 8.0
$ composer require grimzy/laravel-mysql-spatial:^3.0

对于 MySQL 5.7

$ composer require grimzy/laravel-mysql-spatial:^2.0

对于 MySQL 5.6 和 5.5

$ composer require grimzy/laravel-mysql-spatial:^1.0

对于 Laravel 版本低于 5.5 或未使用自动发现时,请在 config/app.php 中注册服务提供器

'providers' => [
  /*
   * Package Service Providers...
   */
  Grimzy\LaravelMysqlSpatial\SpatialServiceProvider::class,
],

快速入门

创建迁移

从命令行

php artisan make:migration create_places_table

然后通过添加至少一个空间数据字段来编辑您刚刚创建的迁移。对于 Laravel 版本低于 5.5,您可以使用此包提供的 Blueprint(Grimzy\LaravelMysqlSpatial\Schema\Blueprint)

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

// For Laravel < 5.5
// use Grimzy\LaravelMysqlSpatial\Schema\Blueprint;

class CreatePlacesTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('places', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('name')->unique();
            // Add a Point spatial data field named location
            $table->point('location')->nullable();
            // Add a Polygon spatial data field named area
            $table->polygon('area')->nullable();
            $table->timestamps();
        });
  
        // Or create the spatial fields with an SRID (e.g. 4326 WGS84 spheroid)
  
        // Schema::create('places', function(Blueprint $table)
        // {
        //     $table->increments('id');
        //     $table->string('name')->unique();
        //     // Add a Point spatial data field named location with SRID 4326
        //     $table->point('location', 4326)->nullable();
        //     // Add a Polygon spatial data field named area with SRID 4326
        //     $table->polygon('area', 4326)->nullable();
        //     $table->timestamps();
        // });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('places');
    }
}

运行迁移

php artisan migrate

创建模型

从命令行

php artisan make:model Place

然后编辑您刚刚创建的模型。它必须使用 SpatialTrait 并定义一个名为 $spatialFields 的数组,包含在迁移中创建的 MySQL 空间数据字段名称(示例在 快速入门 中)

namespace App;

use Illuminate\Database\Eloquent\Model;
use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait;

/**
 * @property \Grimzy\LaravelMysqlSpatial\Types\Point   $location
 * @property \Grimzy\LaravelMysqlSpatial\Types\Polygon $area
 */
class Place extends Model
{
    use SpatialTrait;

    protected $fillable = [
        'name'
    ];

    protected $spatialFields = [
        'location',
        'area'
    ];
}

保存模型

use Grimzy\LaravelMysqlSpatial\Types\Point;
use Grimzy\LaravelMysqlSpatial\Types\Polygon;
use Grimzy\LaravelMysqlSpatial\Types\LineString;

$place1 = new Place();
$place1->name = 'Empire State Building';

// saving a point
$place1->location = new Point(40.7484404, -73.9878441);	// (lat, lng)
$place1->save();

// saving a polygon
$place1->area = new Polygon([new LineString([
    new Point(40.74894149554006, -73.98615270853043),
    new Point(40.74848633046773, -73.98648262023926),
    new Point(40.747925497790725, -73.9851602911949),
    new Point(40.74837050671544, -73.98482501506805),
    new Point(40.74894149554006, -73.98615270853043)
])]);
$place1->save();

或者如果您的数据库字段使用特定的 SRID 创建

use Grimzy\LaravelMysqlSpatial\Types\Point;
use Grimzy\LaravelMysqlSpatial\Types\Polygon;
use Grimzy\LaravelMysqlSpatial\Types\LineString;

$place1 = new Place();
$place1->name = 'Empire State Building';

// saving a point with SRID 4326 (WGS84 spheroid)
$place1->location = new Point(40.7484404, -73.9878441, 4326);	// (lat, lng, srid)
$place1->save();

// saving a polygon with SRID 4326 (WGS84 spheroid)
$place1->area = new Polygon([new LineString([
    new Point(40.74894149554006, -73.98615270853043),
    new Point(40.74848633046773, -73.98648262023926),
    new Point(40.747925497790725, -73.9851602911949),
    new Point(40.74837050671544, -73.98482501506805),
    new Point(40.74894149554006, -73.98615270853043)
])], 4326);
$place1->save();

注意:当保存集合几何体(LineStringPolygonMultiPointMultiLineStringGeometryCollection)时,只有最顶层的几何体应该在构造函数中设置 SRID。

在上面的示例中,当创建一个 new Polygon() 时,我们只在 Polygon 上设置 SRID,并使用默认值对 LineStringPoint 对象。

检索模型

$place2 = Place::first();
$lat = $place2->location->getLat();	// 40.7484404
$lng = $place2->location->getLng();	// -73.9878441

几何类

可用的几何类

查看 类图

使用几何类

为了使您的 Eloquent 模型处理几何类,它必须使用 Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait 特性并定义一个名为 protected 的属性 $spatialFields,它是一个包含 MySQL 空间数据类型列名称的数组(示例在 快速入门 中)

迭代聚合和 ArrayAccess

集合几何体(LineStringPolygonMultiPointMultiLineStringGeometryCollection)实现了 IteratorAggregateArrayAccess,这使得执行迭代器和数组操作变得简单。例如

$polygon = $multipolygon[10];	// ArrayAccess

// IteratorAggregate
for($polygon as $i => $linestring) {
  echo (string) $linestring;
}

辅助函数

从/到已知文本(WKT
// fromWKT($wkt, $srid = 0)
$point = Point::fromWKT('POINT(2 1)');
$point->toWKT();	// POINT(2 1)

$polygon = Polygon::fromWKT('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))');
$polygon->toWKT();	// POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
从/到字符串
// fromString($wkt, $srid = 0)
$point = new Point(1, 2);	// lat, lng
(string)$point			// lng, lat: 2 1

$polygon = Polygon::fromString('(0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)');
(string)$polygon;	// (0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)
从/到 JSON(GeoJSON

几何类实现了 JsonSerializableIlluminate\Contracts\Support\Jsonable,以帮助序列化为 GeoJSON

$point = new Point(40.7484404, -73.9878441);

json_encode($point); // or $point->toJson();

// {
//   "type": "Feature",
//   "properties": {},
//   "geometry": {
//     "type": "Point",
//     "coordinates": [
//       -73.9878441,
//       40.7484404
//     ]
//   }
// }

要将 GeoJSON 字符串反序列化为几何类,可以使用 Geometry::fromJson($json_string)

$location = Geometry::fromJson('{"type":"Point","coordinates":[3.4,1.2]}');
$location instanceof Point::class;  // true
$location->getLat();  // 1.2
$location->getLng()); // 3.4

作用域:空间分析函数

空间分析函数使用 Eloquent Local Scopes 实现。

可用作用域

  • distance($geometryColumn, $geometry, $distance)
  • distanceExcludingSelf($geometryColumn, $geometry, $distance)
  • distanceSphere($geometryColumn, $geometry, $distance)
  • distanceSphereExcludingSelf($geometryColumn, $geometry, $distance)
  • comparison($geometryColumn, $geometry, $relationship)
  • within($geometryColumn, $polygon)
  • crosses($geometryColumn, $geometry)
  • contains($geometryColumn, $geometry)
  • disjoint($geometryColumn, $geometry)
  • equals($geometryColumn, $geometry)
  • intersects($geometryColumn, $geometry)
  • overlaps($geometryColumn, $geometry)
  • doesTouch($geometryColumn, $geometry)
  • orderBySpatial($geometryColumn, $geometry, $orderFunction, $direction = 'asc')
  • orderByDistance($geometryColumn, $geometry, $direction = 'asc')
  • orderByDistanceSphere($geometryColumn, $geometry, $direction = 'asc')

请注意,MySQL 空间分析函数的行为和可用性在每个 MySQL 版本中都有所不同(参见 文档)。

迁移

对于 5.5 版本之前的 Laravel,您可以使用此包提供的 Blueprint:Grimzy\LaravelMysqlSpatial\Schema\Blueprint

use Illuminate\Database\Migrations\Migration;
use Grimzy\LaravelMysqlSpatial\Schema\Blueprint;

class CreatePlacesTable extends Migration {
    // ...
}

可用的 MySQL 空间类型 迁移 Blueprint

  • $table->geometry(string $column_name, int $srid = 0)
  • $table->point(string $column_name, int $srid = 0)
  • $table->lineString(string $column_name, int $srid = 0)
  • $table->polygon(string $column_name, int $srid = 0)
  • $table->multiPoint(string $column_name, int $srid = 0)
  • $table->multiLineString(string $column_name, int $srid = 0)
  • $table->multiPolygon(string $column_name, int $srid = 0)
  • $table->geometryCollection(string $column_name, int $srid = 0)

空间索引

您可以使用 spatialIndexdropSpatialIndex Blueprint 在迁移中添加或删除空间索引。

  • $table->spatialIndex('column_name')
  • $table->dropSpatialIndex(['column_name'])$table->dropSpatialIndex('index_name')

有关空间索引的更多信息,请参阅 MySQL 文档

对于 MyISAM 和(自 MySQL 5.7.5 以来)InnoDB 表,MySQL 可以使用与创建常规索引类似的语法创建空间索引,但使用 SPATIAL 关键字。空间索引中的列必须声明为 NOT NULL

另外,请参阅 Laravel 5.6 文档中的这条 重要说明,关于索引长度。

例如,作为 快速入门 的后续操作;从命令行,生成新的迁移

php artisan make:migration update_places_table

然后编辑您刚刚创建的迁移文件

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class UpdatePlacesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        // MySQL < 5.7.5: table has to be MyISAM
        // \DB::statement('ALTER TABLE places ENGINE = MyISAM');

        Schema::table('places', function (Blueprint $table) {
            // Make sure point is not nullable
            $table->point('location')->change();
          
            // Add a spatial index on the location field
            $table->spatialIndex('location');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('places', function (Blueprint $table) {
            $table->dropSpatialIndex(['location']); // either an array of column names or the index name
        });

        // \DB::statement('ALTER TABLE places ENGINE = InnoDB');

        Schema::table('places', function (Blueprint $table) {
            $table->point('location')->nullable()->change();
        });
    }
}

测试

$ composer test
# or 
$ composer test:unit
$ composer test:integration

集成测试需要运行中的 MySQL 数据库。如果您已安装 Docker,可以轻松启动一个

$ make start_db		# starts MySQL 8.0
# or
$ make start_db V=5.7	# starts MySQL 5.7

贡献

欢迎提出建议和 pull request!带测试的 pull request 是最好的!还有很多 MySQL 空间函数要实现或创造性地使用空间函数的方法。

致谢

最初受到 njbarrett 的 Laravel postgis 包 的启发。