Skip to content

Commit 49b0daf

Browse files
authored
Merge pull request #119 from grimzy/srid
MySQL 8 SRID support
2 parents 7f62564 + e3bcd37 commit 49b0daf

34 files changed

+895
-504
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ php:
66
- '7.0'
77
- '7.1'
88
- '7.2'
9+
- '7.3'
910

1011
env:
11-
- MYSQL_VERSION=5.7
1212
- MYSQL_VERSION=8.0
1313

1414
dist: trusty

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
V=5.7
1+
V=8.0
22
DB_DIR=$(shell pwd)/_db-$(V)
33
mV=10.3
44
mDB_DIR=$(shell pwd)/_db-$(mV)

README.md

Lines changed: 84 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,29 @@ Please check the documentation for your MySQL version. MySQL's Extension for Spa
1313
**Versions**
1414

1515
- `1.x.x`: MySQL 5.6 (also supports MySQL 5.5 but not all spatial analysis functions)
16-
- `2.x.x`: MySQL 5.7 and 8.0
16+
- `2.x.x`: MySQL 5.7
17+
- **`3.x.x`: MySQL 8.0 with SRID support (Current branch)**
1718

1819
This package also works with MariaDB. Please refer to the [MySQL/MariaDB Spatial Support Matrix](https://mariadb.com/kb/en/library/mysqlmariadb-spatial-support-matrix/) for compatibility.
1920

2021
## Installation
2122

2223
Add the package using composer:
2324

25+
```sh
26+
$ composer require grimzy/laravel-mysql-spatial
27+
```
28+
29+
For MySQL 5.7:
30+
2431
```shell
25-
composer require grimzy/laravel-mysql-spatial
32+
$ composer require grimzy/laravel-mysql-spatial:^2.0
2633
```
2734

2835
For MySQL 5.6 and 5.5:
2936

3037
```shell
31-
composer require grimzy/laravel-mysql-spatial:^1.0
38+
$ composer require grimzy/laravel-mysql-spatial:^1.0
3239
```
3340

3441
For Laravel versions before 5.5 or if not using auto-discovery, register the service provider in `config/app.php`:
@@ -80,6 +87,19 @@ class CreatePlacesTable extends Migration {
8087
$table->polygon('area')->nullable();
8188
$table->timestamps();
8289
});
90+
91+
// Or create the spatial fields with an SRID (e.g. 4326 WGS84 spheroid)
92+
93+
// Schema::create('places', function(Blueprint $table)
94+
// {
95+
// $table->increments('id');
96+
// $table->string('name')->unique();
97+
// // Add a Point spatial data field named location with SRID 4326
98+
// $table->point('location', 4326)->nullable();
99+
// // Add a Polygon spatial data field named area with SRID 4326
100+
// $table->polygon('area', 4326)->nullable();
101+
// $table->timestamps();
102+
// });
83103
}
84104

85105
/**
@@ -158,11 +178,37 @@ $place1->area = new Polygon([new LineString([
158178
new Point(40.74894149554006, -73.98615270853043)
159179
])]);
160180
$place1->save();
181+
```
182+
183+
Or if your database fields were created with a specific SRID:
184+
185+
```php
186+
use Grimzy\LaravelMysqlSpatial\Types\Point;
187+
use Grimzy\LaravelMysqlSpatial\Types\Polygon;
188+
use Grimzy\LaravelMysqlSpatial\Types\LineString;
189+
190+
$place1 = new Place();
191+
$place1->name = 'Empire State Building';
161192

162-
$place1->area = new Polygon();
193+
// saving a point with SRID 4326 (WGS84 spheroid)
194+
$place1->location = new Point(40.7484404, -73.9878441, 4326); // (lat, lng, srid)
195+
$place1->save();
163196

197+
// saving a polygon with SRID 4326 (WGS84 spheroid)
198+
$place1->area = new Polygon([new LineString([
199+
new Point(40.74894149554006, -73.98615270853043),
200+
new Point(40.74848633046773, -73.98648262023926),
201+
new Point(40.747925497790725, -73.9851602911949),
202+
new Point(40.74837050671544, -73.98482501506805),
203+
new Point(40.74894149554006, -73.98615270853043)
204+
])], 4326);
205+
$place1->save();
164206
```
165207

208+
> **Note**: When saving collection Geometries (`LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, and `GeometryCollection`), only the top-most geometry should have an SRID set in the constructor.
209+
>
210+
> In the example above, when creating a `new Polygon()`, we only set the SRID on the `Polygon` and use the default for the `LineString` and the `Point` objects.
211+
166212
### Retrieving a model
167213

168214
```php
@@ -177,13 +223,13 @@ $lng = $place2->location->getLng(); // -73.9878441
177223

178224
| Grimzy\LaravelMysqlSpatial\Types | OpenGIS Class |
179225
| ------------------------------------------------------------ | ------------------------------------------------------------ |
180-
| `Point($lat, $lng)` | [Point](https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html) |
181-
| `MultiPoint(Point[])` | [MultiPoint](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipoint.html) |
182-
| `LineString(Point[])` | [LineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-linestring.html) |
183-
| `MultiLineString(LineString[])` | [MultiLineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multilinestring.html) |
184-
| `Polygon(LineString[])` *([exterior and interior boundaries](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html))* | [Polygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html) |
185-
| `MultiPolygon(Polygon[])` | [MultiPolygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipolygon.html) |
186-
| `GeometryCollection(Geometry[])` | [GeometryCollection](https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometrycollection.html) |
226+
| `Point($lat, $lng, $srid = 0)` | [Point](https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html) |
227+
| `MultiPoint(Point[], $srid = 0)` | [MultiPoint](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipoint.html) |
228+
| `LineString(Point[], $srid = 0)` | [LineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-linestring.html) |
229+
| `MultiLineString(LineString[], $srid = 0)` | [MultiLineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multilinestring.html) |
230+
| `Polygon(LineString[], $srid = 0)` *([exterior and interior boundaries](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html))* | [Polygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html) |
231+
| `MultiPolygon(Polygon[], $srid = 0)` | [MultiPolygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipolygon.html) |
232+
| `GeometryCollection(Geometry[], $srid = 0)` | [GeometryCollection](https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometrycollection.html) |
187233

188234
Check out the [Class diagram](https://user-images.githubusercontent.com/1837678/30788608-a5afd894-a16c-11e7-9a51-0a08b331d4c4.png).
189235

@@ -193,7 +239,7 @@ In order for your Eloquent Model to handle the Geometry classes, it must use the
193239

194240
#### IteratorAggregate and ArrayAccess
195241

196-
The "composite" Geometries (`LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, and `GeometryCollection`) implement [`IteratorAggregate`](http://php.net/manual/en/class.iteratoraggregate.php) and [`ArrayAccess`](http://php.net/manual/en/class.arrayaccess.php); making it easy to perform Iterator and Array operations. For example:
242+
The collection Geometries (`LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, and `GeometryCollection`) implement [`IteratorAggregate`](http://php.net/manual/en/class.iteratoraggregate.php) and [`ArrayAccess`](http://php.net/manual/en/class.arrayaccess.php); making it easy to perform Iterator and Array operations. For example:
197243

198244
```php
199245
$polygon = $multipolygon[10]; // ArrayAccess
@@ -207,10 +253,10 @@ for($polygon as $i => $linestring) {
207253

208254
#### Helpers
209255

210-
##### From/To Well Known Text ([WKT](https://dev.mysql.com/doc/refman/5.7/en/gis-data-formats.html#gis-wkt-format))
256+
##### From/To Well Known Text ([WKT](https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-wkt-format))
211257

212258
```php
213-
// fromWKT($wkt)
259+
// fromWKT($wkt, $srid = 0)
214260
$point = Point::fromWKT('POINT(2 1)');
215261
$point->toWKT(); // POINT(2 1)
216262

@@ -221,9 +267,9 @@ $polygon->toWKT(); // POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
221267
##### From/To String
222268

223269
```php
224-
// fromString($wkt)
270+
// fromString($wkt, $srid = 0)
225271
$point = new Point(1, 2); // lat, lng
226-
(string)$point // lng, lat: 2 1
272+
(string)$point // lng, lat: 2 1
227273

228274
$polygon = Polygon::fromString('(0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)');
229275
(string)$polygon; // (0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)
@@ -255,9 +301,9 @@ To deserialize a GeoJSON string into a Geometry class, you can use `Geometry::fr
255301

256302
```php
257303
$location = Geometry::fromJson('{"type":"Point","coordinates":[3.4,1.2]}');
258-
$location instanceof Point::class; // true
259-
$location->getLat(); // 1.2
260-
$location->getLng()); // 3.4
304+
$location instanceof Point::class; // true
305+
$location->getLat(); // 1.2
306+
$location->getLng()); // 3.4
261307
```
262308

263309
## Scopes: Spatial analysis functions
@@ -280,10 +326,10 @@ Available scopes:
280326
- `overlaps($geometryColumn, $geometry)`
281327
- `doesTouch($geometryColumn, $geometry)`
282328
- `orderBySpatial($geometryColumn, $geometry, $orderFunction, $direction = 'asc')`
283-
- `orderByDistance($geometryColumn, $geometry, $direction = 'asc')`
284-
- `orderByDistanceSphere($geometryColumn, $geometry, $direction = 'asc')`
329+
- `orderByDistance($geometryColumn, $geometry, $direction = 'asc')`
330+
- `orderByDistanceSphere($geometryColumn, $geometry, $direction = 'asc')`
285331

286-
*Note that behavior and availability of MySQL spatial analysis functions differs in each MySQL version (cf. [documentation](https://dev.mysql.com/doc/refman/5.7/en/spatial-function-reference.html)).*
332+
*Note that behavior and availability of MySQL spatial analysis functions differs in each MySQL version (cf. [documentation](https://dev.mysql.com/doc/refman/8.0/en/spatial-function-reference.html)).*
287333

288334
## Migrations
289335

@@ -300,16 +346,16 @@ class CreatePlacesTable extends Migration {
300346

301347
### Columns
302348

303-
Available [MySQL Spatial Types](https://dev.mysql.com/doc/refman/5.7/en/spatial-datatypes.html) migration blueprints:
349+
Available [MySQL Spatial Types](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html) migration blueprints:
304350

305-
- `$table->geometry('column_name')`
306-
- `$table->point('column_name')`
307-
- `$table->lineString('column_name')`
308-
- `$table->polygon('column_name')`
309-
- `$table->multiPoint('column_name')`
310-
- `$table->multiLineString('column_name')`
311-
- `$table->multiPolygon('column_name')`
312-
- `$table->geometryCollection('column_name')`
351+
- `$table->geometry(string $column_name, int $srid = 0)`
352+
- `$table->point(string $column_name, int $srid = 0)`
353+
- `$table->lineString(string $column_name, int $srid = 0)`
354+
- `$table->polygon(string $column_name, int $srid = 0)`
355+
- `$table->multiPoint(string $column_name, int $srid = 0)`
356+
- `$table->multiLineString(string $column_name, int $srid = 0)`
357+
- `$table->multiPolygon(string $column_name, int $srid = 0)`
358+
- `$table->geometryCollection(string $column_name, int $srid = 0)`
313359

314360
### Spatial indexes
315361

@@ -318,9 +364,9 @@ You can add or drop spatial indexes in your migrations with the `spatialIndex` a
318364
- `$table->spatialIndex('column_name')`
319365
- `$table->dropSpatialIndex(['column_name'])` or `$table->dropSpatialIndex('index_name')`
320366

321-
Note about spatial indexes from the [MySQL documentation](https://dev.mysql.com/doc/refman/5.7/en/creating-spatial-indexes.html):
367+
Note about spatial indexes from the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/creating-spatial-indexes.html):
322368

323-
> For [`MyISAM`](https://dev.mysql.com/doc/refman/5.7/en/myisam-storage-engine.html) 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 the `SPATIAL` keyword. Columns in spatial indexes must be declared `NOT NULL`.
369+
> For [`MyISAM`](https://dev.mysql.com/doc/refman/8.0/en/myisam-storage-engine.html) 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 the `SPATIAL` keyword. Columns in spatial indexes must be declared `NOT NULL`.
324370
325371
Also please read this [**important note**](https://laravel.com/docs/5.5/migrations#indexes) regarding Index Lengths in the Laravel 5.6 documentation.
326372

@@ -381,18 +427,18 @@ class UpdatePlacesTable extends Migration
381427
## Tests
382428

383429
```shell
384-
composer test
430+
$ composer test
385431
# or
386-
composer test:unit
387-
composer test:integration
432+
$ composer test:unit
433+
$ composer test:integration
388434
```
389435

390436
Integration tests require a running MySQL database. If you have Docker installed, you can start easily start one:
391437

392438
```shell
393-
make start_db # starts MySQL 8.0
439+
$ make start_db # starts MySQL 8.0
394440
# or
395-
make start_db V=5.7 # starts a MySQL 5.7
441+
$ make start_db V=5.7 # starts MySQL 5.7
396442
```
397443

398444
## Contributing

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
},
4444
"extra": {
4545
"branch-alias": {
46-
"dev-master": "2.0.x-dev"
46+
"dev-master": "3.0.x-dev"
4747
},
4848
"laravel": {
4949
"providers": [

src/Eloquent/BaseBuilder.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@ class BaseBuilder extends QueryBuilder
88
{
99
protected function cleanBindings(array $bindings)
1010
{
11-
$bindings = array_map(function ($binding) {
12-
return $binding instanceof SpatialExpression ? $binding->getSpatialValue() : $binding;
13-
}, $bindings);
11+
$spatialBindings = [];
12+
foreach ($bindings as &$binding) {
13+
if ($binding instanceof SpatialExpression) {
14+
$spatialBindings[] = $binding->getSpatialValue();
15+
$spatialBindings[] = $binding->getSrid();
16+
} else {
17+
$spatialBindings[] = $binding;
18+
}
19+
}
1420

15-
return parent::cleanBindings($bindings);
21+
return parent::cleanBindings($spatialBindings);
1622
}
1723
}

src/Eloquent/SpatialExpression.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ class SpatialExpression extends Expression
88
{
99
public function getValue()
1010
{
11-
return 'ST_GeomFromText(?)';
11+
return "ST_GeomFromText(?, ?, 'axis-order=long-lat')";
1212
}
1313

1414
public function getSpatialValue()
1515
{
1616
return $this->value->toWkt();
1717
}
18+
19+
public function getSrid()
20+
{
21+
return $this->value->getSrid();
22+
}
1823
}

src/Eloquent/SpatialTrait.php

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,9 @@ public function scopeDistance($query, $geometryColumn, $geometry, $distance)
134134
{
135135
$this->isColumnAllowed($geometryColumn);
136136

137-
$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) <= ?", [
137+
$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) <= ?", [
138138
$geometry->toWkt(),
139+
$geometry->getSrid(),
139140
$distance,
140141
]);
141142

@@ -148,8 +149,9 @@ public function scopeDistanceExcludingSelf($query, $geometryColumn, $geometry, $
148149

149150
$query = $this->scopeDistance($query, $geometryColumn, $geometry, $distance);
150151

151-
$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) != 0", [
152+
$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) != 0", [
152153
$geometry->toWkt(),
154+
$geometry->getSrid(),
153155
]);
154156

155157
return $query;
@@ -165,17 +167,19 @@ public function scopeDistanceValue($query, $geometryColumn, $geometry)
165167
$query->select('*');
166168
}
167169

168-
$query->selectRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) as distance", [
170+
$query->selectRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) as distance", [
169171
$geometry->toWkt(),
172+
$geometry->getSrid(),
170173
]);
171174
}
172175

173176
public function scopeDistanceSphere($query, $geometryColumn, $geometry, $distance)
174177
{
175178
$this->isColumnAllowed($geometryColumn);
176179

177-
$query->whereRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?)) <= ?", [
180+
$query->whereRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) <= ?", [
178181
$geometry->toWkt(),
182+
$geometry->getSrid(),
179183
$distance,
180184
]);
181185

@@ -188,8 +192,9 @@ public function scopeDistanceSphereExcludingSelf($query, $geometryColumn, $geome
188192

189193
$query = $this->scopeDistanceSphere($query, $geometryColumn, $geometry, $distance);
190194

191-
$query->whereRaw("st_distance_sphere($geometryColumn, ST_GeomFromText(?)) != 0", [
195+
$query->whereRaw("st_distance_sphere($geometryColumn, ST_GeomFromText(?, ?, 'axis-order=long-lat')) != 0", [
192196
$geometry->toWkt(),
197+
$geometry->getSrid(),
193198
]);
194199

195200
return $query;
@@ -204,8 +209,9 @@ public function scopeDistanceSphereValue($query, $geometryColumn, $geometry)
204209
if (!$columns) {
205210
$query->select('*');
206211
}
207-
$query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?)) as distance", [
212+
$query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) as distance", [
208213
$geometry->toWkt(),
214+
$geometry->getSrid(),
209215
]);
210216
}
211217

@@ -217,8 +223,9 @@ public function scopeComparison($query, $geometryColumn, $geometry, $relationshi
217223
throw new UnknownSpatialRelationFunction($relationship);
218224
}
219225

220-
$query->whereRaw("st_{$relationship}(`$geometryColumn`, ST_GeomFromText(?))", [
226+
$query->whereRaw("st_{$relationship}(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat'))", [
221227
$geometry->toWkt(),
228+
$geometry->getSrid(),
222229
]);
223230

224231
return $query;
@@ -272,8 +279,9 @@ public function scopeOrderBySpatial($query, $geometryColumn, $geometry, $orderFu
272279
throw new UnknownSpatialFunctionException($orderFunction);
273280
}
274281

275-
$query->orderByRaw("st_{$orderFunction}(`$geometryColumn`, ST_GeomFromText(?)) {$direction}", [
282+
$query->orderByRaw("st_{$orderFunction}(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) {$direction}", [
276283
$geometry->toWkt(),
284+
$geometry->getSrid(),
277285
]);
278286

279287
return $query;

0 commit comments

Comments
 (0)