- Notifications
You must be signed in to change notification settings - Fork9
MySQL Spatial Data Extension integration with Laravel.
License
limenet/laravel-mysql-spatial
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Laravel package to easily work withMySQL Spatial Data Types andMySQL Spatial Functions.
Please check the documentation for your MySQL version. MySQL's Extension for Spatial Data was added in MySQL 5.5 but many Spatial Functions were changed in 5.6 and 5.7.
This package is a fork ofhttps://github.com/grimzy/laravel-mysql-spatial and virtually all code was written by the contributors to that repo. Thank you!
This package also works with MariaDB. Please refer to theMySQL/MariaDB Spatial Support Matrix for compatibility.
Add the package using composer:
$ composer require limenet/laravel-mysql-spatial
If you need support for older versions, please consider using the packagegrimzy/laravel-mysql-spatial
instead.
- Run
composer remove grimzy/laravel-mysql-spatial
- Run
composer require limenet/laravel-mysql-spatial
- Replace
Grimzy\
withLimenet\
throughout your codebase (most likely, this only affectsuse
statements)
From the command line:
php artisan make:migration create_places_table
Then edit the migration you just created by adding at least one spatial data field.
useIlluminate\Database\Migrations\Migration;useIlluminate\Database\Schema\Blueprint;class CreatePlacesTableextends Migration {publicfunctionup():void { 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();// }); }publicfunctiondown():void { Schema::drop('places'); }}
Run the migration:
php artisan migrate
From the command line:
php artisan make:model Place
Then edit the model you just created. It must use theSpatialTrait
and define an array called$spatialFields
with the name of the MySQL Spatial Data field(s) created in the migration:
namespaceApp;useIlluminate\Database\Eloquent\Model;useLimenet\LaravelMysqlSpatial\Eloquent\SpatialTrait;/** * @property \Limenet\LaravelMysqlSpatial\Types\Point $location * @property \Limenet\LaravelMysqlSpatial\Types\Polygon $area */class Placeextends Model{use SpatialTrait;protected$fillable = ['name' ];protected$spatialFields = ['location','area' ];}
useLimenet\LaravelMysqlSpatial\Types\Point;useLimenet\LaravelMysqlSpatial\Types\Polygon;useLimenet\LaravelMysqlSpatial\Types\LineString;$place1 =newPlace();$place1->name ='Empire State Building';// saving a point$place1->location =newPoint(40.7484404, -73.9878441);// (lat, lng)$place1->save();// saving a polygon$place1->area =newPolygon([newLineString([newPoint(40.74894149554006, -73.98615270853043),newPoint(40.74848633046773, -73.98648262023926),newPoint(40.747925497790725, -73.9851602911949),newPoint(40.74837050671544, -73.98482501506805),newPoint(40.74894149554006, -73.98615270853043)])]);$place1->save();
Or if your database fields were created with a specific SRID:
useLimenet\LaravelMysqlSpatial\Types\Point;useLimenet\LaravelMysqlSpatial\Types\Polygon;useLimenet\LaravelMysqlSpatial\Types\LineString;$place1 =newPlace();$place1->name ='Empire State Building';// saving a point with SRID 4326 (WGS84 spheroid)$place1->location =newPoint(40.7484404, -73.9878441,4326);// (lat, lng, srid)$place1->save();// saving a polygon with SRID 4326 (WGS84 spheroid)$place1->area =newPolygon([newLineString([newPoint(40.74894149554006, -73.98615270853043),newPoint(40.74848633046773, -73.98648262023926),newPoint(40.747925497790725, -73.9851602911949),newPoint(40.74837050671544, -73.98482501506805),newPoint(40.74894149554006, -73.98615270853043)])],4326);$place1->save();
Note: When saving collection Geometries (
LineString
,Polygon
,MultiPoint
,MultiLineString
, andGeometryCollection
), only the top-most geometry should have an SRID set in the constructor.In the example above, when creating a
new Polygon()
, we only set the SRID on thePolygon
and use the default for theLineString
and thePoint
objects.
$place2 = Place::first();$lat =$place2->location->getLat();// 40.7484404$lng =$place2->location->getLng();// -73.9878441
Limenet\LaravelMysqlSpatial\Types | OpenGIS Class |
---|---|
Point($lat, $lng, $srid = 0) | Point |
MultiPoint(Point[], $srid = 0) | MultiPoint |
LineString(Point[], $srid = 0) | LineString |
MultiLineString(LineString[], $srid = 0) | MultiLineString |
Polygon(LineString[], $srid = 0) (exterior and interior boundaries) | Polygon |
MultiPolygon(Polygon[], $srid = 0) | MultiPolygon |
GeometryCollection(Geometry[], $srid = 0) | GeometryCollection |
Check out theClass diagram.
In order for your Eloquent Model to handle the Geometry classes, it must use theLimenet\LaravelMysqlSpatial\Eloquent\SpatialTrait
trait and define aprotected
property$spatialFields
as an array of MySQL Spatial Data Type column names (example inQuickstart).
The collection Geometries (LineString
,Polygon
,MultiPoint
,MultiLineString
, andGeometryCollection
) implementIteratorAggregate
andArrayAccess
; making it easy to perform Iterator and Array operations. For example:
$polygon =$multipolygon[10];// ArrayAccess// IteratorAggregatefor($polygonas$i =>$linestring) {echo (string)$linestring;}
From/To Well Known Text (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 =newPoint(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)
From/To JSON (GeoJSON)
The Geometry classes implementJsonSerializable
andIlluminate\Contracts\Support\Jsonable
to help serialize into GeoJSON:
$point =newPoint(40.7484404, -73.9878441);json_encode($point);// or $point->toJson();// {// "type": "Feature",// "properties": {},// "geometry": {// "type": "Point",// "coordinates": [// -73.9878441,// 40.7484404// ]// }// }
To deserialize a GeoJSON string into a Geometry class, you can useGeometry::fromJson($json_string)
:
$location = Geometry::fromJson('{"type":"Point","coordinates":[3.4,1.2]}');$locationinstanceof Point::class;// true$location->getLat();// 1.2$location->getLng());// 3.4
Spatial analysis functions are implemented usingEloquent Local Scopes.
Available 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')
Note that behavior and availability of MySQL spatial analysis functions differs in each MySQL version (cf.documentation).
AvailableMySQL Spatial Types migration blueprints:
$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)
You can add or drop spatial indexes in your migrations with thespatialIndex
anddropSpatialIndex
blueprints.
$table->spatialIndex('column_name')
$table->dropSpatialIndex(['column_name'])
or$table->dropSpatialIndex('index_name')
Note about spatial indexes from theMySQL documentation:
For
MyISAM
and (as of MySQL 5.7.5)InnoDB
tables, MySQL can create spatial indexes using syntax similar to that for creating regular indexes, but using theSPATIAL
keyword. Columns in spatial indexes must be declaredNOT NULL
.
Also please read thisimportant note regarding Index Lengths in the Laravel 5.6 documentation.
For example, as a follow up to theQuickstart; from the command line, generate a new migration:
php artisan make:migration update_places_table
Then edit the migration file that you just created:
useIlluminate\Database\Migrations\Migration;useIlluminate\Database\Schema\Blueprint;useIlluminate\Support\Facades\Schema;class UpdatePlacesTableextends Migration{/** * Run the migrations. * * @return void */publicfunctionup() {// 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 */publicfunctiondown() { 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(); }); }}
$ composertest
Integration tests require a running MySQL database. If you have Docker installed, you can start easily start one:
$ make start_db# starts MySQL 8.0# or$ make start_db V=5.7# starts MySQL 5.7
Recommendations and pull request are most welcome! Pull requests with tests are the best! There are still a lot of MySQL spatial functions to implement or creative ways to use spatial functions.
Originally inspired fromnjbarrett's Laravel postgis package.
About
MySQL Spatial Data Extension integration with Laravel.
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Languages
- PHP100.0%