├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── composer.json ├── docker-compose.yml ├── phpunit.xml.dist ├── src ├── Connectors │ └── ConnectionFactory.php ├── Doctrine │ ├── Geometry.php │ ├── GeometryCollection.php │ ├── LineString.php │ ├── MultiLineString.php │ ├── MultiPoint.php │ ├── MultiPolygon.php │ ├── Point.php │ └── Polygon.php ├── Eloquent │ ├── BaseBuilder.php │ ├── Builder.php │ ├── SpatialExpression.php │ └── SpatialTrait.php ├── Exceptions │ ├── InvalidGeoJsonException.php │ ├── SpatialFieldsNotDefinedException.php │ ├── UnknownSpatialFunctionException.php │ ├── UnknownSpatialRelationFunction.php │ └── UnknownWKTTypeException.php ├── MysqlConnection.php ├── Schema │ ├── Blueprint.php │ ├── Builder.php │ └── Grammars │ │ └── MySqlGrammar.php ├── SpatialServiceProvider.php └── Types │ ├── Factory.php │ ├── Geometry.php │ ├── GeometryCollection.php │ ├── GeometryInterface.php │ ├── LineString.php │ ├── MultiLineString.php │ ├── MultiPoint.php │ ├── MultiPolygon.php │ ├── Point.php │ ├── PointCollection.php │ └── Polygon.php └── tests ├── Integration ├── IntegrationBaseTestCase.php ├── MigrationTest.php ├── Migrations │ ├── CreateTables.php │ └── UpdateTables.php ├── Models │ ├── GeometryModel.php │ ├── NoSpatialFieldsModel.php │ └── WithSridModel.php ├── SpatialTest.php └── SridSpatialTest.php └── Unit ├── BaseTestCase.php ├── Connectors └── ConnectionFactoryTest.php ├── Eloquent ├── BuilderTest.php └── SpatialTraitTest.php ├── MysqlConnectionTest.php ├── Schema ├── BlueprintTest.php ├── BuilderTest.php └── Grammars │ └── MySqlGrammarTest.php ├── Stubs └── PDOStub.php └── Types ├── GeometryCollectionTest.php ├── GeometryTest.php ├── LineStringTest.php ├── MultiLineStringTest.php ├── MultiPointTest.php ├── MultiPolygonTest.php ├── PointTest.php └── PolygonTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor/ 3 | composer.lock 4 | _db/ 5 | build/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - '7.3' 5 | - '7.4' 6 | 7 | env: 8 | - MYSQL_VERSION=8.0 9 | 10 | dist: trusty 11 | 12 | sudo: required 13 | 14 | services: 15 | - docker 16 | 17 | before_install: 18 | - echo "memory_limit=3G" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini 19 | - sudo /etc/init.d/mysql stop 20 | - make start_db V=$MYSQL_VERSION 21 | 22 | install: composer install 23 | 24 | before_script: 25 | - mkdir -p build/logs 26 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 27 | - chmod +x ./cc-test-reporter 28 | - ./cc-test-reporter before-build 29 | 30 | script: vendor/bin/phpunit --coverage-clover build/logs/clover.xml 31 | 32 | after_script: ./cc-test-reporter after-build --coverage-input-type clover --exit-code $TRAVIS_TEST_RESULT 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Original work Copyright (c) 2015 Peter Haza 4 | Modified work Copyright (c) 2017 Joseph Estefane 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | V=8.0 2 | DB_DIR=$(shell pwd)/_db-$(V) 3 | mV=10.3 4 | mDB_DIR=$(shell pwd)/_db-$(mV) 5 | 6 | start_db: 7 | @echo Starting MySQL $(V) 8 | docker run --rm -d --name spatial-mysql \ 9 | -p 3306:3306 \ 10 | -v $(DB_DIR):/var/lib/mysql \ 11 | -e MYSQL_DATABASE=spatial_test \ 12 | -e MYSQL_ALLOW_EMPTY_PASSWORD=yes \ 13 | mysql:$(V) --character-set-server=utf8 --collation-server=utf8_general_ci --default-authentication-plugin=mysql_native_password 14 | 15 | start_db_maria: 16 | @echo Starting MariaDB $(mV) 17 | docker run --rm -d --name spatial-mysql \ 18 | -p 3306:3306 \ 19 | -v $(DB_DIR):/var/lib/mysql \ 20 | -e MYSQL_DATABASE=spatial_test \ 21 | -e MYSQL_ALLOW_EMPTY_PASSWORD=yes \ 22 | mariadb:$(mV) --character-set-server=utf8 --collation-server=utf8_general_ci --default-authentication-plugin=mysql_native_password 23 | 24 | 25 | rm_db: 26 | docker stop spatial-mysql || true 27 | rm -Rf $(DB_DIR) 28 | 29 | refresh_db: rm_db start_db 30 | 31 | get_ip: 32 | @docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' spatial-mysql -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel MySQL Spatial extension 2 | 3 | [![Build Status](https://img.shields.io/travis/grimzy/laravel-mysql-spatial.svg?style=flat-square)](https://travis-ci.org/grimzy/laravel-mysql-spatial) 4 | [![Code Climate](https://img.shields.io/codeclimate/maintainability/grimzy/laravel-mysql-spatial.svg?style=flat-square)](https://codeclimate.com/github/grimzy/laravel-mysql-spatial/maintainability) 5 | [![Code Climate](https://img.shields.io/codeclimate/c/grimzy/laravel-mysql-spatial.svg?style=flat-square&colorB=4BCA2A)](https://codeclimate.com/github/grimzy/laravel-mysql-spatial/test_coverage) [![Packagist](https://img.shields.io/packagist/v/grimzy/laravel-mysql-spatial.svg?style=flat-square)](https://packagist.org/packages/grimzy/laravel-mysql-spatial) 6 | [![Packagist](https://img.shields.io/packagist/dt/grimzy/laravel-mysql-spatial.svg?style=flat-square)](https://packagist.org/packages/grimzy/laravel-mysql-spatial) [![StyleCI](https://github.styleci.io/repos/83766141/shield?branch=master)](https://github.styleci.io/repos/83766141) 7 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=flat-square)](LICENSE) 8 | 9 | Laravel package to easily work with [MySQL Spatial Data Types](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html) and [MySQL Spatial Functions](https://dev.mysql.com/doc/refman/8.0/en/spatial-function-reference.html). 10 | 11 | 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. 12 | 13 | **Versions** 14 | 15 | - `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 (Laravel version < 8.0) 17 | - `3.x.x`: MySQL 8.0 with SRID support (Laravel version < 8.0) 18 | - **`4.x.x`: MySQL 8.0 with SRID support (Laravel 8+) [Current branch]** 19 | - `5.x.x`: MySQL 5.7 and 8.0 (Laravel 8+) 20 | 21 | 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. 22 | 23 | ## Installation 24 | 25 | Add the package using composer: 26 | 27 | ```sh 28 | $ composer require grimzy/laravel-mysql-spatial:^4.0 29 | 30 | # or for Laravel version < 8.0 31 | $ composer require grimzy/laravel-mysql-spatial:^3.0 32 | ``` 33 | 34 | For MySQL 5.7: 35 | 36 | ```shell 37 | $ composer require grimzy/laravel-mysql-spatial:^2.0 38 | ``` 39 | 40 | For MySQL 5.6 and 5.5: 41 | 42 | ```shell 43 | $ composer require grimzy/laravel-mysql-spatial:^1.0 44 | ``` 45 | 46 | For Laravel versions before 5.5 or if not using auto-discovery, register the service provider in `config/app.php`: 47 | 48 | ```php 49 | 'providers' => [ 50 | /* 51 | * Package Service Providers... 52 | */ 53 | Grimzy\LaravelMysqlSpatial\SpatialServiceProvider::class, 54 | ], 55 | ``` 56 | 57 | ## Quickstart 58 | 59 | ### Create a migration 60 | 61 | From the command line: 62 | 63 | ```shell 64 | php artisan make:migration create_places_table 65 | ``` 66 | 67 | Then edit the migration you just created by adding at least one spatial data field. For Laravel versions prior to 5.5, you can use the Blueprint provided by this package (Grimzy\LaravelMysqlSpatial\Schema\Blueprint): 68 | 69 | ```php 70 | use Illuminate\Database\Migrations\Migration; 71 | use Illuminate\Database\Schema\Blueprint; 72 | 73 | // For Laravel < 5.5 74 | // use Grimzy\LaravelMysqlSpatial\Schema\Blueprint; 75 | 76 | class CreatePlacesTable extends Migration { 77 | 78 | /** 79 | * Run the migrations. 80 | * 81 | * @return void 82 | */ 83 | public function up() 84 | { 85 | Schema::create('places', function(Blueprint $table) 86 | { 87 | $table->increments('id'); 88 | $table->string('name')->unique(); 89 | // Add a Point spatial data field named location 90 | $table->point('location')->nullable(); 91 | // Add a Polygon spatial data field named area 92 | $table->polygon('area')->nullable(); 93 | $table->timestamps(); 94 | }); 95 | 96 | // Or create the spatial fields with an SRID (e.g. 4326 WGS84 spheroid) 97 | 98 | // Schema::create('places', function(Blueprint $table) 99 | // { 100 | // $table->increments('id'); 101 | // $table->string('name')->unique(); 102 | // // Add a Point spatial data field named location with SRID 4326 103 | // $table->point('location', 4326)->nullable(); 104 | // // Add a Polygon spatial data field named area with SRID 4326 105 | // $table->polygon('area', 4326)->nullable(); 106 | // $table->timestamps(); 107 | // }); 108 | } 109 | 110 | /** 111 | * Reverse the migrations. 112 | * 113 | * @return void 114 | */ 115 | public function down() 116 | { 117 | Schema::drop('places'); 118 | } 119 | } 120 | ``` 121 | 122 | Run the migration: 123 | 124 | ```shell 125 | php artisan migrate 126 | ``` 127 | 128 | ### Create a model 129 | 130 | From the command line: 131 | 132 | ```shell 133 | php artisan make:model Place 134 | ``` 135 | 136 | Then edit the model you just created. It must use the `SpatialTrait` and define an array called `$spatialFields` with the name of the MySQL Spatial Data field(s) created in the migration: 137 | 138 | ```php 139 | namespace App; 140 | 141 | use Illuminate\Database\Eloquent\Model; 142 | use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait; 143 | 144 | /** 145 | * @property \Grimzy\LaravelMysqlSpatial\Types\Point $location 146 | * @property \Grimzy\LaravelMysqlSpatial\Types\Polygon $area 147 | */ 148 | class Place extends Model 149 | { 150 | use SpatialTrait; 151 | 152 | protected $fillable = [ 153 | 'name' 154 | ]; 155 | 156 | protected $spatialFields = [ 157 | 'location', 158 | 'area' 159 | ]; 160 | } 161 | ``` 162 | 163 | ### Saving a model 164 | 165 | ```php 166 | use Grimzy\LaravelMysqlSpatial\Types\Point; 167 | use Grimzy\LaravelMysqlSpatial\Types\Polygon; 168 | use Grimzy\LaravelMysqlSpatial\Types\LineString; 169 | 170 | $place1 = new Place(); 171 | $place1->name = 'Empire State Building'; 172 | 173 | // saving a point 174 | $place1->location = new Point(40.7484404, -73.9878441); // (lat, lng) 175 | $place1->save(); 176 | 177 | // saving a polygon 178 | $place1->area = new Polygon([new LineString([ 179 | new Point(40.74894149554006, -73.98615270853043), 180 | new Point(40.74848633046773, -73.98648262023926), 181 | new Point(40.747925497790725, -73.9851602911949), 182 | new Point(40.74837050671544, -73.98482501506805), 183 | new Point(40.74894149554006, -73.98615270853043) 184 | ])]); 185 | $place1->save(); 186 | ``` 187 | 188 | Or if your database fields were created with a specific SRID: 189 | 190 | ```php 191 | use Grimzy\LaravelMysqlSpatial\Types\Point; 192 | use Grimzy\LaravelMysqlSpatial\Types\Polygon; 193 | use Grimzy\LaravelMysqlSpatial\Types\LineString; 194 | 195 | $place1 = new Place(); 196 | $place1->name = 'Empire State Building'; 197 | 198 | // saving a point with SRID 4326 (WGS84 spheroid) 199 | $place1->location = new Point(40.7484404, -73.9878441, 4326); // (lat, lng, srid) 200 | $place1->save(); 201 | 202 | // saving a polygon with SRID 4326 (WGS84 spheroid) 203 | $place1->area = new Polygon([new LineString([ 204 | new Point(40.74894149554006, -73.98615270853043), 205 | new Point(40.74848633046773, -73.98648262023926), 206 | new Point(40.747925497790725, -73.9851602911949), 207 | new Point(40.74837050671544, -73.98482501506805), 208 | new Point(40.74894149554006, -73.98615270853043) 209 | ])], 4326); 210 | $place1->save(); 211 | ``` 212 | 213 | > **Note**: When saving collection Geometries (`LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, and `GeometryCollection`), only the top-most geometry should have an SRID set in the constructor. 214 | > 215 | > 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. 216 | 217 | ### Retrieving a model 218 | 219 | ```php 220 | $place2 = Place::first(); 221 | $lat = $place2->location->getLat(); // 40.7484404 222 | $lng = $place2->location->getLng(); // -73.9878441 223 | ``` 224 | 225 | ## Geometry classes 226 | 227 | ### Available Geometry classes 228 | 229 | | Grimzy\LaravelMysqlSpatial\Types | OpenGIS Class | 230 | | ------------------------------------------------------------ | ------------------------------------------------------------ | 231 | | `Point($lat, $lng, $srid = 0)` | [Point](https://dev.mysql.com/doc/refman/8.0/en/gis-class-point.html) | 232 | | `MultiPoint(Point[], $srid = 0)` | [MultiPoint](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipoint.html) | 233 | | `LineString(Point[], $srid = 0)` | [LineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-linestring.html) | 234 | | `MultiLineString(LineString[], $srid = 0)` | [MultiLineString](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multilinestring.html) | 235 | | `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) | 236 | | `MultiPolygon(Polygon[], $srid = 0)` | [MultiPolygon](https://dev.mysql.com/doc/refman/8.0/en/gis-class-multipolygon.html) | 237 | | `GeometryCollection(Geometry[], $srid = 0)` | [GeometryCollection](https://dev.mysql.com/doc/refman/8.0/en/gis-class-geometrycollection.html) | 238 | 239 | Check out the [Class diagram](https://user-images.githubusercontent.com/1837678/30788608-a5afd894-a16c-11e7-9a51-0a08b331d4c4.png). 240 | 241 | ### Using Geometry classes 242 | 243 | In order for your Eloquent Model to handle the Geometry classes, it must use the `Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait` trait and define a `protected` property `$spatialFields` as an array of MySQL Spatial Data Type column names (example in [Quickstart](#user-content-create-a-model)). 244 | 245 | #### IteratorAggregate and ArrayAccess 246 | 247 | 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: 248 | 249 | ```php 250 | $polygon = $multipolygon[10]; // ArrayAccess 251 | 252 | // IteratorAggregate 253 | for($polygon as $i => $linestring) { 254 | echo (string) $linestring; 255 | } 256 | 257 | ``` 258 | 259 | #### Helpers 260 | 261 | ##### From/To Well Known Text ([WKT](https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-wkt-format)) 262 | 263 | ```php 264 | // fromWKT($wkt, $srid = 0) 265 | $point = Point::fromWKT('POINT(2 1)'); 266 | $point->toWKT(); // POINT(2 1) 267 | 268 | $polygon = Polygon::fromWKT('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))'); 269 | $polygon->toWKT(); // POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)) 270 | ``` 271 | 272 | ##### From/To String 273 | 274 | ```php 275 | // fromString($wkt, $srid = 0) 276 | $point = new Point(1, 2); // lat, lng 277 | (string)$point // lng, lat: 2 1 278 | 279 | $polygon = Polygon::fromString('(0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)'); 280 | (string)$polygon; // (0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1) 281 | ``` 282 | 283 | ##### From/To JSON ([GeoJSON](http://geojson.org/)) 284 | 285 | The Geometry classes implement [`JsonSerializable`](http://php.net/manual/en/class.jsonserializable.php) and `Illuminate\Contracts\Support\Jsonable` to help serialize into GeoJSON: 286 | 287 | ```php 288 | $point = new Point(40.7484404, -73.9878441); 289 | 290 | json_encode($point); // or $point->toJson(); 291 | 292 | // { 293 | // "type": "Feature", 294 | // "properties": {}, 295 | // "geometry": { 296 | // "type": "Point", 297 | // "coordinates": [ 298 | // -73.9878441, 299 | // 40.7484404 300 | // ] 301 | // } 302 | // } 303 | ``` 304 | 305 | To deserialize a GeoJSON string into a Geometry class, you can use `Geometry::fromJson($json_string)` : 306 | 307 | ```php 308 | $location = Geometry::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 309 | $location instanceof Point::class; // true 310 | $location->getLat(); // 1.2 311 | $location->getLng()); // 3.4 312 | ``` 313 | 314 | ## Scopes: Spatial analysis functions 315 | 316 | Spatial analysis functions are implemented using [Eloquent Local Scopes](https://laravel.com/docs/5.4/eloquent#local-scopes). 317 | 318 | Available scopes: 319 | 320 | - `distance($geometryColumn, $geometry, $distance)` 321 | - `distanceExcludingSelf($geometryColumn, $geometry, $distance)` 322 | - `distanceSphere($geometryColumn, $geometry, $distance)` 323 | - `distanceSphereExcludingSelf($geometryColumn, $geometry, $distance)` 324 | - `comparison($geometryColumn, $geometry, $relationship)` 325 | - `within($geometryColumn, $polygon)` 326 | - `crosses($geometryColumn, $geometry)` 327 | - `contains($geometryColumn, $geometry)` 328 | - `disjoint($geometryColumn, $geometry)` 329 | - `equals($geometryColumn, $geometry)` 330 | - `intersects($geometryColumn, $geometry)` 331 | - `overlaps($geometryColumn, $geometry)` 332 | - `doesTouch($geometryColumn, $geometry)` 333 | - `orderBySpatial($geometryColumn, $geometry, $orderFunction, $direction = 'asc')` 334 | - `orderByDistance($geometryColumn, $geometry, $direction = 'asc')` 335 | - `orderByDistanceSphere($geometryColumn, $geometry, $direction = 'asc')` 336 | 337 | *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)).* 338 | 339 | ## Migrations 340 | 341 | For Laravel versions prior to 5.5, you can use the Blueprint provided with this package: `Grimzy\LaravelMysqlSpatial\Schema\Blueprint`. 342 | 343 | ```php 344 | use Illuminate\Database\Migrations\Migration; 345 | use Grimzy\LaravelMysqlSpatial\Schema\Blueprint; 346 | 347 | class CreatePlacesTable extends Migration { 348 | // ... 349 | } 350 | ``` 351 | 352 | ### Columns 353 | 354 | Available [MySQL Spatial Types](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html) migration blueprints: 355 | 356 | - `$table->geometry(string $column_name, int $srid = 0)` 357 | - `$table->point(string $column_name, int $srid = 0)` 358 | - `$table->lineString(string $column_name, int $srid = 0)` 359 | - `$table->polygon(string $column_name, int $srid = 0)` 360 | - `$table->multiPoint(string $column_name, int $srid = 0)` 361 | - `$table->multiLineString(string $column_name, int $srid = 0)` 362 | - `$table->multiPolygon(string $column_name, int $srid = 0)` 363 | - `$table->geometryCollection(string $column_name, int $srid = 0)` 364 | 365 | ### Spatial indexes 366 | 367 | You can add or drop spatial indexes in your migrations with the `spatialIndex` and `dropSpatialIndex` blueprints. 368 | 369 | - `$table->spatialIndex('column_name')` 370 | - `$table->dropSpatialIndex(['column_name'])` or `$table->dropSpatialIndex('index_name')` 371 | 372 | Note about spatial indexes from the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/creating-spatial-indexes.html): 373 | 374 | > 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`. 375 | 376 | Also please read this [**important note**](https://laravel.com/docs/5.5/migrations#indexes) regarding Index Lengths in the Laravel 5.6 documentation. 377 | 378 | For example, as a follow up to the [Quickstart](#user-content-create-a-migration); from the command line, generate a new migration: 379 | 380 | ```shell 381 | php artisan make:migration update_places_table 382 | ``` 383 | 384 | Then edit the migration file that you just created: 385 | 386 | ```php 387 | use Illuminate\Database\Migrations\Migration; 388 | use Illuminate\Database\Schema\Blueprint; 389 | use Illuminate\Support\Facades\Schema; 390 | 391 | class UpdatePlacesTable extends Migration 392 | { 393 | /** 394 | * Run the migrations. 395 | * 396 | * @return void 397 | */ 398 | public function up() 399 | { 400 | // MySQL < 5.7.5: table has to be MyISAM 401 | // \DB::statement('ALTER TABLE places ENGINE = MyISAM'); 402 | 403 | Schema::table('places', function (Blueprint $table) { 404 | // Make sure point is not nullable 405 | $table->point('location')->change(); 406 | 407 | // Add a spatial index on the location field 408 | $table->spatialIndex('location'); 409 | }); 410 | } 411 | 412 | /** 413 | * Reverse the migrations. 414 | * 415 | * @return void 416 | */ 417 | public function down() 418 | { 419 | Schema::table('places', function (Blueprint $table) { 420 | $table->dropSpatialIndex(['location']); // either an array of column names or the index name 421 | }); 422 | 423 | // \DB::statement('ALTER TABLE places ENGINE = InnoDB'); 424 | 425 | Schema::table('places', function (Blueprint $table) { 426 | $table->point('location')->nullable()->change(); 427 | }); 428 | } 429 | } 430 | ``` 431 | 432 | ## Tests 433 | 434 | ```shell 435 | $ composer test 436 | # or 437 | $ composer test:unit 438 | $ composer test:integration 439 | ``` 440 | 441 | Integration tests require a running MySQL database. If you have Docker installed, you can start easily start one: 442 | 443 | ```shell 444 | $ make start_db # starts MySQL 8.0 445 | # or 446 | $ make start_db V=5.7 # starts MySQL 5.7 447 | ``` 448 | 449 | ## Contributing 450 | 451 | 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. 452 | 453 | ## Credits 454 | 455 | Originally inspired from [njbarrett's Laravel postgis package](https://github.com/njbarrett/laravel-postgis). 456 | 457 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grimzy/laravel-mysql-spatial", 3 | "description": "MySQL spatial data types extension for Laravel.", 4 | "scripts": { 5 | "test": "phpunit -c phpunit.xml.dist", 6 | "test:unit": "phpunit -c phpunit.xml.dist --testsuite unit", 7 | "test:integration": "phpunit -c phpunit.xml.dist --testsuite integration" 8 | }, 9 | "type": "library", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Joseph Estefane", 14 | "email": "estefanejoe@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.3", 19 | "ext-pdo": "*", 20 | "ext-json": "*", 21 | "illuminate/database": "^8.0", 22 | "geo-io/wkb-parser": "^1.0", 23 | "jmikola/geojson": "^1.0" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit": "~6.5", 27 | "laravel/laravel": "^8.0", 28 | "doctrine/dbal": "^2.5", 29 | "laravel/browser-kit-testing": "^2.0", 30 | "mockery/mockery": "^1.3" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Grimzy\\LaravelMysqlSpatial\\": "src/" 35 | } 36 | }, 37 | "autoload-dev" : { 38 | "classmap" : [ 39 | "tests/Unit", 40 | "tests/Integration" 41 | ] 42 | }, 43 | "extra": { 44 | "branch-alias": { 45 | "dev-master": "4.0.x-dev" 46 | }, 47 | "laravel": { 48 | "providers": [ 49 | "Grimzy\\LaravelMysqlSpatial\\SpatialServiceProvider" 50 | ] 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | db: 4 | image: mysql:5.7 5 | ports: 6 | - "3306:3306" 7 | environment: 8 | MYSQL_DATABASE: 'spatial_test' 9 | MYSQL_ROOT_PASSWORD: '' 10 | MYSQL_ALLOW_EMPTY_PASSWORD: 1 -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/Unit 14 | 15 | 16 | ./tests/Integration 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | ./src 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/Connectors/ConnectionFactory.php: -------------------------------------------------------------------------------- 1 | container->bound($key = "db.connection.{$driver}")) { 23 | return $this->container->make($key, [$connection, $database, $prefix, $config]); // @codeCoverageIgnore 24 | } 25 | 26 | if ($driver === 'mysql') { 27 | return new MysqlConnection($connection, $database, $prefix, $config); 28 | } 29 | 30 | return parent::createConnection($driver, $connection, $database, $prefix, $config); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Doctrine/Geometry.php: -------------------------------------------------------------------------------- 1 | getSpatialValue(); 15 | $spatialBindings[] = $binding->getSrid(); 16 | } else { 17 | $spatialBindings[] = $binding; 18 | } 19 | } 20 | 21 | return parent::cleanBindings($spatialBindings); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Eloquent/Builder.php: -------------------------------------------------------------------------------- 1 | &$value) { 13 | if ($value instanceof GeometryInterface) { 14 | $value = $this->asWKT($value); 15 | } 16 | } 17 | 18 | return parent::update($values); 19 | } 20 | 21 | protected function asWKT(GeometryInterface $geometry) 22 | { 23 | return new SpatialExpression($geometry); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Eloquent/SpatialExpression.php: -------------------------------------------------------------------------------- 1 | value->toWkt(); 17 | } 18 | 19 | public function getSrid() 20 | { 21 | return $this->value->getSrid(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Eloquent/SpatialTrait.php: -------------------------------------------------------------------------------- 1 | getConnection(); 76 | 77 | return new BaseBuilder( 78 | $connection, 79 | $connection->getQueryGrammar(), 80 | $connection->getPostProcessor() 81 | ); 82 | } 83 | 84 | protected function performInsert(EloquentBuilder $query, array $options = []) 85 | { 86 | foreach ($this->attributes as $key => $value) { 87 | if ($value instanceof GeometryInterface) { 88 | $this->geometries[$key] = $value; //Preserve the geometry objects prior to the insert 89 | $this->attributes[$key] = new SpatialExpression($value); 90 | } 91 | } 92 | 93 | $insert = parent::performInsert($query, $options); 94 | 95 | foreach ($this->geometries as $key => $value) { 96 | $this->attributes[$key] = $value; //Retrieve the geometry objects so they can be used in the model 97 | } 98 | 99 | return $insert; //Return the result of the parent insert 100 | } 101 | 102 | public function setRawAttributes(array $attributes, $sync = false) 103 | { 104 | $spatial_fields = $this->getSpatialFields(); 105 | 106 | foreach ($attributes as $attribute => &$value) { 107 | if (in_array($attribute, $spatial_fields) && is_string($value) && strlen($value) >= 13) { 108 | $value = Geometry::fromWKB($value); 109 | } 110 | } 111 | 112 | return parent::setRawAttributes($attributes, $sync); 113 | } 114 | 115 | public function getSpatialFields() 116 | { 117 | if (property_exists($this, 'spatialFields')) { 118 | return $this->spatialFields; 119 | } else { 120 | throw new SpatialFieldsNotDefinedException(__CLASS__.' has to define $spatialFields'); 121 | } 122 | } 123 | 124 | public function isColumnAllowed($geometryColumn) 125 | { 126 | if (!in_array($geometryColumn, $this->getSpatialFields())) { 127 | throw new SpatialFieldsNotDefinedException(); 128 | } 129 | 130 | return true; 131 | } 132 | 133 | public function scopeDistance($query, $geometryColumn, $geometry, $distance) 134 | { 135 | $this->isColumnAllowed($geometryColumn); 136 | 137 | $query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) <= ?", [ 138 | $geometry->toWkt(), 139 | $geometry->getSrid(), 140 | $distance, 141 | ]); 142 | 143 | return $query; 144 | } 145 | 146 | public function scopeDistanceExcludingSelf($query, $geometryColumn, $geometry, $distance) 147 | { 148 | $this->isColumnAllowed($geometryColumn); 149 | 150 | $query = $this->scopeDistance($query, $geometryColumn, $geometry, $distance); 151 | 152 | $query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) != 0", [ 153 | $geometry->toWkt(), 154 | $geometry->getSrid(), 155 | ]); 156 | 157 | return $query; 158 | } 159 | 160 | public function scopeDistanceValue($query, $geometryColumn, $geometry) 161 | { 162 | $this->isColumnAllowed($geometryColumn); 163 | 164 | $columns = $query->getQuery()->columns; 165 | 166 | if (!$columns) { 167 | $query->select('*'); 168 | } 169 | 170 | $query->selectRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) as distance", [ 171 | $geometry->toWkt(), 172 | $geometry->getSrid(), 173 | ]); 174 | } 175 | 176 | public function scopeDistanceSphere($query, $geometryColumn, $geometry, $distance) 177 | { 178 | $this->isColumnAllowed($geometryColumn); 179 | 180 | $query->whereRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) <= ?", [ 181 | $geometry->toWkt(), 182 | $geometry->getSrid(), 183 | $distance, 184 | ]); 185 | 186 | return $query; 187 | } 188 | 189 | public function scopeDistanceSphereExcludingSelf($query, $geometryColumn, $geometry, $distance) 190 | { 191 | $this->isColumnAllowed($geometryColumn); 192 | 193 | $query = $this->scopeDistanceSphere($query, $geometryColumn, $geometry, $distance); 194 | 195 | $query->whereRaw("st_distance_sphere($geometryColumn, ST_GeomFromText(?, ?, 'axis-order=long-lat')) != 0", [ 196 | $geometry->toWkt(), 197 | $geometry->getSrid(), 198 | ]); 199 | 200 | return $query; 201 | } 202 | 203 | public function scopeDistanceSphereValue($query, $geometryColumn, $geometry) 204 | { 205 | $this->isColumnAllowed($geometryColumn); 206 | 207 | $columns = $query->getQuery()->columns; 208 | 209 | if (!$columns) { 210 | $query->select('*'); 211 | } 212 | $query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) as distance", [ 213 | $geometry->toWkt(), 214 | $geometry->getSrid(), 215 | ]); 216 | } 217 | 218 | public function scopeComparison($query, $geometryColumn, $geometry, $relationship) 219 | { 220 | $this->isColumnAllowed($geometryColumn); 221 | 222 | if (!in_array($relationship, $this->stRelations)) { 223 | throw new UnknownSpatialRelationFunction($relationship); 224 | } 225 | 226 | $query->whereRaw("st_{$relationship}(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat'))", [ 227 | $geometry->toWkt(), 228 | $geometry->getSrid(), 229 | ]); 230 | 231 | return $query; 232 | } 233 | 234 | public function scopeWithin($query, $geometryColumn, $polygon) 235 | { 236 | return $this->scopeComparison($query, $geometryColumn, $polygon, 'within'); 237 | } 238 | 239 | public function scopeCrosses($query, $geometryColumn, $geometry) 240 | { 241 | return $this->scopeComparison($query, $geometryColumn, $geometry, 'crosses'); 242 | } 243 | 244 | public function scopeContains($query, $geometryColumn, $geometry) 245 | { 246 | return $this->scopeComparison($query, $geometryColumn, $geometry, 'contains'); 247 | } 248 | 249 | public function scopeDisjoint($query, $geometryColumn, $geometry) 250 | { 251 | return $this->scopeComparison($query, $geometryColumn, $geometry, 'disjoint'); 252 | } 253 | 254 | public function scopeEquals($query, $geometryColumn, $geometry) 255 | { 256 | return $this->scopeComparison($query, $geometryColumn, $geometry, 'equals'); 257 | } 258 | 259 | public function scopeIntersects($query, $geometryColumn, $geometry) 260 | { 261 | return $this->scopeComparison($query, $geometryColumn, $geometry, 'intersects'); 262 | } 263 | 264 | public function scopeOverlaps($query, $geometryColumn, $geometry) 265 | { 266 | return $this->scopeComparison($query, $geometryColumn, $geometry, 'overlaps'); 267 | } 268 | 269 | public function scopeDoesTouch($query, $geometryColumn, $geometry) 270 | { 271 | return $this->scopeComparison($query, $geometryColumn, $geometry, 'touches'); 272 | } 273 | 274 | public function scopeOrderBySpatial($query, $geometryColumn, $geometry, $orderFunction, $direction = 'asc') 275 | { 276 | $this->isColumnAllowed($geometryColumn); 277 | 278 | if (!in_array($orderFunction, $this->stOrderFunctions)) { 279 | throw new UnknownSpatialFunctionException($orderFunction); 280 | } 281 | 282 | $query->orderByRaw("st_{$orderFunction}(`$geometryColumn`, ST_GeomFromText(?, ?, 'axis-order=long-lat')) {$direction}", [ 283 | $geometry->toWkt(), 284 | $geometry->getSrid(), 285 | ]); 286 | 287 | return $query; 288 | } 289 | 290 | public function scopeOrderByDistance($query, $geometryColumn, $geometry, $direction = 'asc') 291 | { 292 | return $this->scopeOrderBySpatial($query, $geometryColumn, $geometry, 'distance', $direction); 293 | } 294 | 295 | public function scopeOrderByDistanceSphere($query, $geometryColumn, $geometry, $direction = 'asc') 296 | { 297 | return $this->scopeOrderBySpatial($query, $geometryColumn, $geometry, 'distance_sphere', $direction); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidGeoJsonException.php: -------------------------------------------------------------------------------- 1 | getDoctrineSchemaManager()->getDatabasePlatform(); 30 | foreach ($geometries as $type) { 31 | $dbPlatform->registerDoctrineTypeMapping($type, 'string'); 32 | } 33 | } 34 | } 35 | 36 | /** 37 | * Get the default schema grammar instance. 38 | * 39 | * @return \Illuminate\Database\Grammar 40 | */ 41 | protected function getDefaultSchemaGrammar() 42 | { 43 | return $this->withTablePrefix(new MySqlGrammar()); 44 | } 45 | 46 | /** 47 | * Get a schema builder instance for the connection. 48 | * 49 | * @return \Illuminate\Database\Schema\MySqlBuilder 50 | */ 51 | public function getSchemaBuilder() 52 | { 53 | if (is_null($this->schemaGrammar)) { 54 | $this->useDefaultSchemaGrammar(); 55 | } 56 | 57 | return new Builder($this); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Schema/Blueprint.php: -------------------------------------------------------------------------------- 1 | addColumn('geometry', $column, compact('srid')); 20 | } 21 | 22 | /** 23 | * Add a point column on the table. 24 | * 25 | * @param string $column 26 | * @param null|int $srid 27 | * 28 | * @return \Illuminate\Support\Fluent 29 | */ 30 | public function point($column, $srid = null) 31 | { 32 | return $this->addColumn('point', $column, compact('srid')); 33 | } 34 | 35 | /** 36 | * Add a linestring column on the table. 37 | * 38 | * @param string $column 39 | * @param null|int $srid 40 | * 41 | * @return \Illuminate\Support\Fluent 42 | */ 43 | public function lineString($column, $srid = null) 44 | { 45 | return $this->addColumn('linestring', $column, compact('srid')); 46 | } 47 | 48 | /** 49 | * Add a polygon column on the table. 50 | * 51 | * @param string $column 52 | * @param null|int $srid 53 | * 54 | * @return \Illuminate\Support\Fluent 55 | */ 56 | public function polygon($column, $srid = null) 57 | { 58 | return $this->addColumn('polygon', $column, compact('srid')); 59 | } 60 | 61 | /** 62 | * Add a multipoint column on the table. 63 | * 64 | * @param string $column 65 | * @param null|int $srid 66 | * 67 | * @return \Illuminate\Support\Fluent 68 | */ 69 | public function multiPoint($column, $srid = null) 70 | { 71 | return $this->addColumn('multipoint', $column, compact('srid')); 72 | } 73 | 74 | /** 75 | * Add a multilinestring column on the table. 76 | * 77 | * @param string $column 78 | * @param null|int $srid 79 | * 80 | * @return \Illuminate\Support\Fluent 81 | */ 82 | public function multiLineString($column, $srid = null) 83 | { 84 | return $this->addColumn('multilinestring', $column, compact('srid')); 85 | } 86 | 87 | /** 88 | * Add a multipolygon column on the table. 89 | * 90 | * @param string $column 91 | * @param null|int $srid 92 | * 93 | * @return \Illuminate\Support\Fluent 94 | */ 95 | public function multiPolygon($column, $srid = null) 96 | { 97 | return $this->addColumn('multipolygon', $column, compact('srid')); 98 | } 99 | 100 | /** 101 | * Add a geometrycollection column on the table. 102 | * 103 | * @param string $column 104 | * @param null|int $srid 105 | * 106 | * @return \Illuminate\Support\Fluent 107 | */ 108 | public function geometryCollection($column, $srid = null) 109 | { 110 | return $this->addColumn('geometrycollection', $column, compact('srid')); 111 | } 112 | 113 | /** 114 | * Specify a spatial index for the table. 115 | * 116 | * @param string|array $columns 117 | * @param string $name 118 | * 119 | * @return \Illuminate\Support\Fluent 120 | */ 121 | public function spatialIndex($columns, $name = null) 122 | { 123 | return $this->indexCommand('spatial', $columns, $name); 124 | } 125 | 126 | /** 127 | * Indicate that the given index should be dropped. 128 | * 129 | * @param string|array $index 130 | * 131 | * @return \Illuminate\Support\Fluent 132 | */ 133 | public function dropSpatialIndex($index) 134 | { 135 | return $this->dropIndexCommand('dropIndex', 'spatial', $index); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/Schema/Builder.php: -------------------------------------------------------------------------------- 1 | modifiers)) { 17 | $this->modifiers[] = self::COLUMN_MODIFIER_SRID; 18 | } 19 | } 20 | 21 | /** 22 | * Adds a statement to add a geometry column. 23 | * 24 | * @param Fluent $column 25 | * 26 | * @return string 27 | */ 28 | public function typeGeometry(Fluent $column) 29 | { 30 | return 'GEOMETRY'; 31 | } 32 | 33 | /** 34 | * Adds a statement to add a point column. 35 | * 36 | * @param Fluent $column 37 | * 38 | * @return string 39 | */ 40 | public function typePoint(Fluent $column) 41 | { 42 | return 'POINT'; 43 | } 44 | 45 | /** 46 | * Adds a statement to add a linestring column. 47 | * 48 | * @param Fluent $column 49 | * 50 | * @return string 51 | */ 52 | public function typeLinestring(Fluent $column) 53 | { 54 | return 'LINESTRING'; 55 | } 56 | 57 | /** 58 | * Adds a statement to add a polygon column. 59 | * 60 | * @param Fluent $column 61 | * 62 | * @return string 63 | */ 64 | public function typePolygon(Fluent $column) 65 | { 66 | return 'POLYGON'; 67 | } 68 | 69 | /** 70 | * Adds a statement to add a multipoint column. 71 | * 72 | * @param Fluent $column 73 | * 74 | * @return string 75 | */ 76 | public function typeMultipoint(Fluent $column) 77 | { 78 | return 'MULTIPOINT'; 79 | } 80 | 81 | /** 82 | * Adds a statement to add a multilinestring column. 83 | * 84 | * @param Fluent $column 85 | * 86 | * @return string 87 | */ 88 | public function typeMultilinestring(Fluent $column) 89 | { 90 | return 'MULTILINESTRING'; 91 | } 92 | 93 | /** 94 | * Adds a statement to add a multipolygon column. 95 | * 96 | * @param Fluent $column 97 | * 98 | * @return string 99 | */ 100 | public function typeMultipolygon(Fluent $column) 101 | { 102 | return 'MULTIPOLYGON'; 103 | } 104 | 105 | /** 106 | * Adds a statement to add a geometrycollection column. 107 | * 108 | * @param Fluent $column 109 | * 110 | * @return string 111 | */ 112 | public function typeGeometrycollection(Fluent $column) 113 | { 114 | return 'GEOMETRYCOLLECTION'; 115 | } 116 | 117 | /** 118 | * Compile a spatial index key command. 119 | * 120 | * @param Blueprint $blueprint 121 | * @param Fluent $command 122 | * 123 | * @return string 124 | */ 125 | public function compileSpatial(Blueprint $blueprint, Fluent $command) 126 | { 127 | return $this->compileKey($blueprint, $command, 'spatial'); 128 | } 129 | 130 | /** 131 | * Get the SQL for a SRID column modifier. 132 | * 133 | * @param \Illuminate\Database\Schema\Blueprint $blueprint 134 | * @param Fluent $column 135 | * 136 | * @return string|null 137 | */ 138 | protected function modifySrid(\Illuminate\Database\Schema\Blueprint $blueprint, Fluent $column) 139 | { 140 | if (!is_null($column->srid) && is_int($column->srid) && $column->srid > 0) { 141 | return ' srid '.$column->srid; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/SpatialServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('db.factory', function ($app) { 34 | return new ConnectionFactory($app); 35 | }); 36 | 37 | // The database manager is used to resolve various connections, since multiple 38 | // connections might be managed. It also implements the connection resolver 39 | // interface which may be used by other components requiring connections. 40 | $this->app->singleton('db', function ($app) { 41 | return new DatabaseManager($app, $app['db.factory']); 42 | }); 43 | 44 | if (class_exists(DoctrineType::class)) { 45 | // Prevent geometry type fields from throwing a 'type not found' error when changing them 46 | $geometries = [ 47 | 'geometry' => Geometry::class, 48 | 'point' => Point::class, 49 | 'linestring' => LineString::class, 50 | 'polygon' => Polygon::class, 51 | 'multipoint' => MultiPoint::class, 52 | 'multilinestring' => MultiLineString::class, 53 | 'multipolygon' => MultiPolygon::class, 54 | 'geometrycollection' => GeometryCollection::class, 55 | ]; 56 | $typeNames = array_keys(DoctrineType::getTypesMap()); 57 | foreach ($geometries as $type => $class) { 58 | if (!in_array($type, $typeNames)) { 59 | DoctrineType::addType($type, $class); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Types/Factory.php: -------------------------------------------------------------------------------- 1 | Point::class, 14 | 2 => LineString::class, 15 | 3 => Polygon::class, 16 | 4 => MultiPoint::class, 17 | 5 => MultiLineString::class, 18 | 6 => MultiPolygon::class, 19 | 7 => GeometryCollection::class, 20 | ]; 21 | 22 | protected $srid; 23 | 24 | public function __construct($srid = 0) 25 | { 26 | $this->srid = (int) $srid; 27 | } 28 | 29 | public function getSrid() 30 | { 31 | return $this->srid; 32 | } 33 | 34 | public function setSrid($srid) 35 | { 36 | $this->srid = (int) $srid; 37 | } 38 | 39 | public static function getWKTArgument($value) 40 | { 41 | $left = strpos($value, '('); 42 | $right = strrpos($value, ')'); 43 | 44 | return substr($value, $left + 1, $right - $left - 1); 45 | } 46 | 47 | public static function getWKTClass($value) 48 | { 49 | $left = strpos($value, '('); 50 | $type = trim(substr($value, 0, $left)); 51 | 52 | switch (strtoupper($type)) { 53 | case 'POINT': 54 | return Point::class; 55 | case 'LINESTRING': 56 | return LineString::class; 57 | case 'POLYGON': 58 | return Polygon::class; 59 | case 'MULTIPOINT': 60 | return MultiPoint::class; 61 | case 'MULTILINESTRING': 62 | return MultiLineString::class; 63 | case 'MULTIPOLYGON': 64 | return MultiPolygon::class; 65 | case 'GEOMETRYCOLLECTION': 66 | return GeometryCollection::class; 67 | default: 68 | throw new UnknownWKTTypeException('Type was '.$type); 69 | } 70 | } 71 | 72 | public static function fromWKB($wkb) 73 | { 74 | $srid = substr($wkb, 0, 4); 75 | $srid = unpack('L', $srid)[1]; 76 | 77 | $wkb = substr($wkb, 4); 78 | $parser = new Parser(new Factory()); 79 | 80 | /** @var Geometry $parsed */ 81 | $parsed = $parser->parse($wkb); 82 | 83 | if ($srid > 0) { 84 | $parsed->setSrid($srid); 85 | } 86 | 87 | return $parsed; 88 | } 89 | 90 | public static function fromWKT($wkt, $srid = null) 91 | { 92 | $wktArgument = static::getWKTArgument($wkt); 93 | 94 | return static::fromString($wktArgument, $srid); 95 | } 96 | 97 | public static function fromJson($geoJson) 98 | { 99 | if (is_string($geoJson)) { 100 | $geoJson = GeoJson::jsonUnserialize(json_decode($geoJson)); 101 | } 102 | 103 | if ($geoJson->getType() === 'FeatureCollection') { 104 | return GeometryCollection::fromJson($geoJson); 105 | } 106 | 107 | if ($geoJson->getType() === 'Feature') { 108 | $geoJson = $geoJson->getGeometry(); 109 | } 110 | 111 | $type = '\Grimzy\LaravelMysqlSpatial\Types\\'.$geoJson->getType(); 112 | 113 | return $type::fromJson($geoJson); 114 | } 115 | 116 | public function toJson($options = 0) 117 | { 118 | return json_encode($this, $options); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Types/GeometryCollection.php: -------------------------------------------------------------------------------- 1 | validateItems($geometries); 49 | 50 | $this->items = $geometries; 51 | } 52 | 53 | public function getGeometries() 54 | { 55 | return $this->items; 56 | } 57 | 58 | public function toWKT() 59 | { 60 | return sprintf('GEOMETRYCOLLECTION(%s)', (string) $this); 61 | } 62 | 63 | public function __toString() 64 | { 65 | return implode(',', array_map(function (GeometryInterface $geometry) { 66 | return $geometry->toWKT(); 67 | }, $this->items)); 68 | } 69 | 70 | public static function fromString($wktArgument, $srid = 0) 71 | { 72 | if (empty($wktArgument)) { 73 | return new static([]); 74 | } 75 | 76 | $geometry_strings = preg_split('/,\s*(?=[A-Za-z])/', $wktArgument); 77 | 78 | return new static(array_map(function ($geometry_string) { 79 | $klass = Geometry::getWKTClass($geometry_string); 80 | 81 | return call_user_func($klass.'::fromWKT', $geometry_string); 82 | }, $geometry_strings), $srid); 83 | } 84 | 85 | public function toArray() 86 | { 87 | return $this->items; 88 | } 89 | 90 | public function getIterator() 91 | { 92 | return new ArrayIterator($this->items); 93 | } 94 | 95 | public function offsetExists($offset) 96 | { 97 | return isset($this->items[$offset]); 98 | } 99 | 100 | public function offsetGet($offset) 101 | { 102 | return $this->offsetExists($offset) ? $this->items[$offset] : null; 103 | } 104 | 105 | public function offsetSet($offset, $value) 106 | { 107 | $this->validateItemType($value); 108 | 109 | if (is_null($offset)) { 110 | $this->items[] = $value; 111 | } else { 112 | $this->items[$offset] = $value; 113 | } 114 | } 115 | 116 | public function offsetUnset($offset) 117 | { 118 | unset($this->items[$offset]); 119 | } 120 | 121 | public function count() 122 | { 123 | return count($this->items); 124 | } 125 | 126 | public static function fromJson($geoJson) 127 | { 128 | if (is_string($geoJson)) { 129 | $geoJson = GeoJson::jsonUnserialize(json_decode($geoJson)); 130 | } 131 | 132 | if (!is_a($geoJson, FeatureCollection::class)) { 133 | throw new InvalidGeoJsonException('Expected '.FeatureCollection::class.', got '.get_class($geoJson)); 134 | } 135 | 136 | $set = []; 137 | foreach ($geoJson->getFeatures() as $feature) { 138 | $set[] = parent::fromJson($feature); 139 | } 140 | 141 | return new self($set); 142 | } 143 | 144 | /** 145 | * Convert to GeoJson GeometryCollection that is jsonable to GeoJSON. 146 | * 147 | * @return \GeoJson\Geometry\GeometryCollection 148 | */ 149 | public function jsonSerialize() 150 | { 151 | $geometries = []; 152 | foreach ($this->items as $geometry) { 153 | $geometries[] = $geometry->jsonSerialize(); 154 | } 155 | 156 | return new \GeoJson\Geometry\GeometryCollection($geometries); 157 | } 158 | 159 | /** 160 | * Checks whether the items are valid to create this collection. 161 | * 162 | * @param array $items 163 | */ 164 | protected function validateItems(array $items) 165 | { 166 | $this->validateItemCount($items); 167 | 168 | foreach ($items as $item) { 169 | $this->validateItemType($item); 170 | } 171 | } 172 | 173 | /** 174 | * Checks whether the array has enough items to generate a valid WKT. 175 | * 176 | * @param array $items 177 | * 178 | * @see $minimumCollectionItems 179 | */ 180 | protected function validateItemCount(array $items) 181 | { 182 | if (count($items) < $this->minimumCollectionItems) { 183 | $entries = $this->minimumCollectionItems === 1 ? 'entry' : 'entries'; 184 | 185 | throw new InvalidArgumentException(sprintf( 186 | '%s must contain at least %d %s', 187 | get_class($this), 188 | $this->minimumCollectionItems, 189 | $entries 190 | )); 191 | } 192 | } 193 | 194 | /** 195 | * Checks the type of the items in the array. 196 | * 197 | * @param $item 198 | * 199 | * @see $collectionItemType 200 | */ 201 | protected function validateItemType($item) 202 | { 203 | if (!$item instanceof $this->collectionItemType) { 204 | throw new InvalidArgumentException(sprintf( 205 | '%s must be a collection of %s', 206 | get_class($this), 207 | $this->collectionItemType 208 | )); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/Types/GeometryInterface.php: -------------------------------------------------------------------------------- 1 | toPairList()); 21 | } 22 | 23 | public static function fromWkt($wkt, $srid = 0) 24 | { 25 | $wktArgument = Geometry::getWKTArgument($wkt); 26 | 27 | return static::fromString($wktArgument, $srid); 28 | } 29 | 30 | public static function fromString($wktArgument, $srid = 0) 31 | { 32 | $pairs = explode(',', trim($wktArgument)); 33 | $points = array_map(function ($pair) { 34 | return Point::fromPair($pair); 35 | }, $pairs); 36 | 37 | return new static($points, $srid); 38 | } 39 | 40 | public function __toString() 41 | { 42 | return $this->toPairList(); 43 | } 44 | 45 | public static function fromJson($geoJson) 46 | { 47 | if (is_string($geoJson)) { 48 | $geoJson = GeoJson::jsonUnserialize(json_decode($geoJson)); 49 | } 50 | 51 | if (!is_a($geoJson, GeoJsonLineString::class)) { 52 | throw new InvalidGeoJsonException('Expected '.GeoJsonLineString::class.', got '.get_class($geoJson)); 53 | } 54 | 55 | $set = []; 56 | foreach ($geoJson->getCoordinates() as $coordinate) { 57 | $set[] = new Point($coordinate[1], $coordinate[0]); 58 | } 59 | 60 | return new self($set); 61 | } 62 | 63 | /** 64 | * Convert to GeoJson LineString that is jsonable to GeoJSON. 65 | * 66 | * @return \GeoJson\Geometry\LineString 67 | */ 68 | public function jsonSerialize() 69 | { 70 | $points = []; 71 | foreach ($this->items as $point) { 72 | $points[] = $point->jsonSerialize(); 73 | } 74 | 75 | return new GeoJsonLineString($points); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Types/MultiLineString.php: -------------------------------------------------------------------------------- 1 | items; 28 | } 29 | 30 | public function toWKT() 31 | { 32 | return sprintf('MULTILINESTRING(%s)', (string) $this); 33 | } 34 | 35 | public static function fromString($wktArgument, $srid = 0) 36 | { 37 | $str = preg_split('/\)\s*,\s*\(/', substr(trim($wktArgument), 1, -1)); 38 | $lineStrings = array_map(function ($data) { 39 | return LineString::fromString($data); 40 | }, $str); 41 | 42 | return new static($lineStrings, $srid); 43 | } 44 | 45 | public function __toString() 46 | { 47 | return implode(',', array_map(function (LineString $lineString) { 48 | return sprintf('(%s)', (string) $lineString); 49 | }, $this->getLineStrings())); 50 | } 51 | 52 | public function offsetSet($offset, $value) 53 | { 54 | $this->validateItemType($value); 55 | 56 | parent::offsetSet($offset, $value); 57 | } 58 | 59 | public static function fromJson($geoJson) 60 | { 61 | if (is_string($geoJson)) { 62 | $geoJson = GeoJson::jsonUnserialize(json_decode($geoJson)); 63 | } 64 | 65 | if (!is_a($geoJson, GeoJsonMultiLineString::class)) { 66 | throw new InvalidGeoJsonException('Expected '.GeoJsonMultiLineString::class.', got '.get_class($geoJson)); 67 | } 68 | 69 | $set = []; 70 | foreach ($geoJson->getCoordinates() as $coordinates) { 71 | $points = []; 72 | foreach ($coordinates as $coordinate) { 73 | $points[] = new Point($coordinate[1], $coordinate[0]); 74 | } 75 | $set[] = new LineString($points); 76 | } 77 | 78 | return new self($set); 79 | } 80 | 81 | /** 82 | * Convert to GeoJson Point that is jsonable to GeoJSON. 83 | * 84 | * @return \GeoJson\Geometry\MultiLineString 85 | */ 86 | public function jsonSerialize() 87 | { 88 | $lineStrings = []; 89 | 90 | foreach ($this->items as $lineString) { 91 | $lineStrings[] = $lineString->jsonSerialize(); 92 | } 93 | 94 | return new GeoJsonMultiLineString($lineStrings); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Types/MultiPoint.php: -------------------------------------------------------------------------------- 1 | toPair()); 46 | }, $this->items)); 47 | } 48 | 49 | public static function fromJson($geoJson) 50 | { 51 | if (is_string($geoJson)) { 52 | $geoJson = GeoJson::jsonUnserialize(json_decode($geoJson)); 53 | } 54 | 55 | if (!is_a($geoJson, GeoJsonMultiPoint::class)) { 56 | throw new InvalidGeoJsonException('Expected '.GeoJsonMultiPoint::class.', got '.get_class($geoJson)); 57 | } 58 | 59 | $set = []; 60 | foreach ($geoJson->getCoordinates() as $coordinate) { 61 | $set[] = new Point($coordinate[1], $coordinate[0]); 62 | } 63 | 64 | return new self($set); 65 | } 66 | 67 | /** 68 | * Convert to GeoJson MultiPoint that is jsonable to GeoJSON. 69 | * 70 | * @return \GeoJson\Geometry\MultiPoint 71 | */ 72 | public function jsonSerialize() 73 | { 74 | $points = []; 75 | foreach ($this->items as $point) { 76 | $points[] = $point->jsonSerialize(); 77 | } 78 | 79 | return new GeoJsonMultiPoint($points); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Types/MultiPolygon.php: -------------------------------------------------------------------------------- 1 | items)); 35 | } 36 | 37 | public static function fromString($wktArgument, $srid = 0) 38 | { 39 | $parts = preg_split('/(\)\s*\)\s*,\s*\(\s*\()/', $wktArgument, -1, PREG_SPLIT_DELIM_CAPTURE); 40 | $polygons = static::assembleParts($parts); 41 | 42 | return new static(array_map(function ($polygonString) { 43 | return Polygon::fromString($polygonString); 44 | }, $polygons), $srid); 45 | } 46 | 47 | /** 48 | * Get the polygons that make up this MultiPolygon. 49 | * 50 | * @return array|Polygon[] 51 | */ 52 | public function getPolygons() 53 | { 54 | return $this->items; 55 | } 56 | 57 | /** 58 | * Make an array like this: 59 | * "((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1", 60 | * ")), ((", 61 | * "-1 -1,-1 -2,-2 -2,-2 -1,-1 -1", 62 | * ")), ((", 63 | * "-1 -1,-1 -2,-2 -2,-2 -1,-1 -1))". 64 | * 65 | * Into: 66 | * "((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1))", 67 | * "((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1))", 68 | * "((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1))" 69 | * 70 | * @param array $parts 71 | * 72 | * @return array 73 | */ 74 | protected static function assembleParts(array $parts) 75 | { 76 | $polygons = []; 77 | $count = count($parts); 78 | 79 | for ($i = 0; $i < $count; $i++) { 80 | if ($i % 2 !== 0) { 81 | list($end, $start) = explode(',', $parts[$i]); 82 | $polygons[$i - 1] .= $end; 83 | $polygons[++$i] = $start.$parts[$i]; 84 | } else { 85 | $polygons[] = $parts[$i]; 86 | } 87 | } 88 | 89 | return $polygons; 90 | } 91 | 92 | public function offsetSet($offset, $value) 93 | { 94 | $this->validateItemType($value); 95 | 96 | parent::offsetSet($offset, $value); 97 | } 98 | 99 | public static function fromJson($geoJson) 100 | { 101 | if (is_string($geoJson)) { 102 | $geoJson = GeoJson::jsonUnserialize(json_decode($geoJson)); 103 | } 104 | 105 | if (!is_a($geoJson, GeoJsonMultiPolygon::class)) { 106 | throw new InvalidGeoJsonException('Expected '.GeoJsonMultiPolygon::class.', got '.get_class($geoJson)); 107 | } 108 | 109 | $set = []; 110 | foreach ($geoJson->getCoordinates() as $polygonCoordinates) { 111 | $lineStrings = []; 112 | foreach ($polygonCoordinates as $lineStringCoordinates) { 113 | $points = []; 114 | foreach ($lineStringCoordinates as $lineStringCoordinate) { 115 | $points[] = new Point($lineStringCoordinate[1], $lineStringCoordinate[0]); 116 | } 117 | $lineStrings[] = new LineString($points); 118 | } 119 | $set[] = new Polygon($lineStrings); 120 | } 121 | 122 | return new self($set); 123 | } 124 | 125 | /** 126 | * Convert to GeoJson MultiPolygon that is jsonable to GeoJSON. 127 | * 128 | * @return \GeoJson\Geometry\MultiPolygon 129 | */ 130 | public function jsonSerialize() 131 | { 132 | $polygons = []; 133 | foreach ($this->items as $polygon) { 134 | $polygons[] = $polygon->jsonSerialize(); 135 | } 136 | 137 | return new GeoJsonMultiPolygon($polygons); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Types/Point.php: -------------------------------------------------------------------------------- 1 | lat = (float) $lat; 20 | $this->lng = (float) $lng; 21 | } 22 | 23 | public function getLat() 24 | { 25 | return $this->lat; 26 | } 27 | 28 | public function setLat($lat) 29 | { 30 | $this->lat = (float) $lat; 31 | } 32 | 33 | public function getLng() 34 | { 35 | return $this->lng; 36 | } 37 | 38 | public function setLng($lng) 39 | { 40 | $this->lng = (float) $lng; 41 | } 42 | 43 | public function toPair() 44 | { 45 | return $this->getLng().' '.$this->getLat(); 46 | } 47 | 48 | public static function fromPair($pair, $srid = 0) 49 | { 50 | list($lng, $lat) = explode(' ', trim($pair, "\t\n\r \x0B()")); 51 | 52 | return new static((float) $lat, (float) $lng, (int) $srid); 53 | } 54 | 55 | public function toWKT() 56 | { 57 | return sprintf('POINT(%s)', (string) $this); 58 | } 59 | 60 | public static function fromString($wktArgument, $srid = 0) 61 | { 62 | return static::fromPair($wktArgument, $srid); 63 | } 64 | 65 | public function __toString() 66 | { 67 | return $this->getLng().' '.$this->getLat(); 68 | } 69 | 70 | /** 71 | * @param $geoJson \GeoJson\Feature\Feature|string 72 | * 73 | * @return \Grimzy\LaravelMysqlSpatial\Types\Point 74 | */ 75 | public static function fromJson($geoJson) 76 | { 77 | if (is_string($geoJson)) { 78 | $geoJson = GeoJson::jsonUnserialize(json_decode($geoJson)); 79 | } 80 | 81 | if (!is_a($geoJson, GeoJsonPoint::class)) { 82 | throw new InvalidGeoJsonException('Expected '.GeoJsonPoint::class.', got '.get_class($geoJson)); 83 | } 84 | 85 | $coordinates = $geoJson->getCoordinates(); 86 | 87 | return new self($coordinates[1], $coordinates[0]); 88 | } 89 | 90 | /** 91 | * Convert to GeoJson Point that is jsonable to GeoJSON. 92 | * 93 | * @return \GeoJson\Geometry\Point 94 | */ 95 | public function jsonSerialize() 96 | { 97 | return new GeoJsonPoint([$this->getLng(), $this->getLat()]); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Types/PointCollection.php: -------------------------------------------------------------------------------- 1 | toPair(); 21 | }, $this->items)); 22 | } 23 | 24 | public function offsetSet($offset, $value) 25 | { 26 | $this->validateItemType($value); 27 | 28 | parent::offsetSet($offset, $value); 29 | } 30 | 31 | /** 32 | * @return array|\Grimzy\LaravelMysqlSpatial\Types\Point[] 33 | */ 34 | public function getPoints() 35 | { 36 | return $this->items; 37 | } 38 | 39 | /** 40 | * @param \Grimzy\LaravelMysqlSpatial\Types\Point $point 41 | * 42 | * @deprecated 2.1.0 Use array_unshift($multipoint, $point); instead 43 | * @see array_unshift 44 | * @see ArrayAccess 45 | */ 46 | public function prependPoint(Point $point) 47 | { 48 | array_unshift($this->items, $point); 49 | } 50 | 51 | /** 52 | * @param \Grimzy\LaravelMysqlSpatial\Types\Point $point 53 | * 54 | * @deprecated 2.1.0 Use $multipoint[] = $point; instead 55 | * @see ArrayAccess 56 | */ 57 | public function appendPoint(Point $point) 58 | { 59 | $this->items[] = $point; 60 | } 61 | 62 | /** 63 | * @param $index 64 | * @param \Grimzy\LaravelMysqlSpatial\Types\Point $point 65 | * 66 | * @deprecated 2.1.0 Use array_splice($multipoint, $index, 0, [$point]); instead 67 | * @see array_splice 68 | * @see ArrayAccess 69 | */ 70 | public function insertPoint($index, Point $point) 71 | { 72 | if (count($this->items) - 1 < $index) { 73 | throw new InvalidArgumentException('$index is greater than the size of the array'); 74 | } 75 | 76 | array_splice($this->items, $index, 0, [$point]); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Types/Polygon.php: -------------------------------------------------------------------------------- 1 | getCoordinates() as $coordinates) { 28 | $points = []; 29 | foreach ($coordinates as $coordinate) { 30 | $points[] = new Point($coordinate[1], $coordinate[0]); 31 | } 32 | $set[] = new LineString($points); 33 | } 34 | 35 | return new self($set); 36 | } 37 | 38 | /** 39 | * Convert to GeoJson Polygon that is jsonable to GeoJSON. 40 | * 41 | * @return \GeoJson\Geometry\Polygon 42 | */ 43 | public function jsonSerialize() 44 | { 45 | $linearRings = []; 46 | foreach ($this->items as $lineString) { 47 | $linearRings[] = new \GeoJson\Geometry\LinearRing($lineString->jsonSerialize()->getCoordinates()); 48 | } 49 | 50 | return new GeoJsonPolygon($linearRings); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Integration/IntegrationBaseTestCase.php: -------------------------------------------------------------------------------- 1 | register(SpatialServiceProvider::class); 21 | 22 | $app->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); 23 | 24 | $app['config']->set('database.default', 'mysql'); 25 | $app['config']->set('database.connections.mysql.host', env('DB_HOST')); 26 | $app['config']->set('database.connections.mysql.port', env('DB_PORT')); 27 | $app['config']->set('database.connections.mysql.database', env('DB_DATABASE')); 28 | $app['config']->set('database.connections.mysql.username', env('DB_USERNAME')); 29 | $app['config']->set('database.connections.mysql.password', env('DB_PASSWORD')); 30 | $app['config']->set('database.connections.mysql.modes', [ 31 | 'ONLY_FULL_GROUP_BY', 32 | 'STRICT_TRANS_TABLES', 33 | 'NO_ZERO_IN_DATE', 34 | 'NO_ZERO_DATE', 35 | 'ERROR_FOR_DIVISION_BY_ZERO', 36 | 'NO_ENGINE_SUBSTITUTION', 37 | ]); 38 | 39 | return $app; 40 | } 41 | 42 | /** 43 | * Setup DB before each test. 44 | * 45 | * @return void 46 | */ 47 | public function setUp() 48 | { 49 | parent::setUp(); 50 | 51 | $this->after_fix = $this->isMySQL8AfterFix(); 52 | 53 | $this->onMigrations(function ($migrationClass) { 54 | (new $migrationClass())->up(); 55 | }); 56 | 57 | //\DB::listen(function($sql) { 58 | // var_dump($sql); 59 | //}); 60 | } 61 | 62 | public function tearDown() 63 | { 64 | $this->onMigrations(function ($migrationClass) { 65 | (new $migrationClass())->down(); 66 | }, true); 67 | 68 | parent::tearDown(); 69 | } 70 | 71 | // MySQL 8.0.4 fixed bug #26941370 and bug #88031 72 | private function isMySQL8AfterFix() 73 | { 74 | $results = DB::select(DB::raw('select version()')); 75 | $mysql_version = $results[0]->{'version()'}; 76 | 77 | return version_compare($mysql_version, '8.0.4', '>='); 78 | } 79 | 80 | protected function assertDatabaseHas($table, array $data, $connection = null) 81 | { 82 | if (method_exists($this, 'seeInDatabase')) { 83 | $this->seeInDatabase($table, $data, $connection); 84 | } else { 85 | parent::assertDatabaseHas($table, $data, $connection); 86 | } 87 | } 88 | 89 | protected function assertException($exceptionName, $exceptionMessage = null) 90 | { 91 | if (method_exists(parent::class, 'expectException')) { 92 | parent::expectException($exceptionName); 93 | if (!is_null($exceptionMessage)) { 94 | $this->expectExceptionMessage($exceptionMessage); 95 | } 96 | } else { 97 | $this->setExpectedException($exceptionName, $exceptionMessage); 98 | } 99 | } 100 | 101 | private function onMigrations(\Closure $closure, $reverse_sort = false) 102 | { 103 | $migrations = $this->migrations; 104 | $reverse_sort ? rsort($migrations, SORT_STRING) : sort($migrations, SORT_STRING); 105 | 106 | foreach ($migrations as $migrationClass) { 107 | $closure($migrationClass); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tests/Integration/MigrationTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('geometry', $result->Table); 33 | $this->assertEquals($expected, $result->{'Create Table'}); 34 | } 35 | 36 | public function testTableWasCreatedWithSrid() 37 | { 38 | $result = DB::selectOne('SHOW CREATE TABLE with_srid'); 39 | 40 | $expected = 'CREATE TABLE `with_srid` ( 41 | `id` int unsigned NOT NULL AUTO_INCREMENT, 42 | `geo` geometry /*!80003 SRID 3857 */ DEFAULT NULL, 43 | `location` point /*!80003 SRID 3857 */ DEFAULT NULL, 44 | `line` linestring /*!80003 SRID 3857 */ DEFAULT NULL, 45 | `shape` polygon /*!80003 SRID 3857 */ DEFAULT NULL, 46 | `multi_locations` multipoint /*!80003 SRID 3857 */ DEFAULT NULL, 47 | `multi_lines` multilinestring /*!80003 SRID 3857 */ DEFAULT NULL, 48 | `multi_shapes` multipolygon /*!80003 SRID 3857 */ DEFAULT NULL, 49 | `multi_geometries` geomcollection /*!80003 SRID 3857 */ DEFAULT NULL, 50 | PRIMARY KEY (`id`) 51 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'; 52 | 53 | $this->assertEquals('with_srid', $result->Table); 54 | $this->assertEquals($expected, $result->{'Create Table'}); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Integration/Migrations/CreateTables.php: -------------------------------------------------------------------------------- 1 | charset = 'utf8mb4'; 18 | $table->collation = 'utf8mb4_unicode_ci'; 19 | $table->increments('id'); 20 | $table->geometry('geo')->default(null)->nullable(); 21 | $table->point('location'); // required to be not null in order to add an index 22 | $table->lineString('line')->default(null)->nullable(); 23 | $table->polygon('shape')->default(null)->nullable(); 24 | $table->multiPoint('multi_locations')->default(null)->nullable(); 25 | $table->multiLineString('multi_lines')->default(null)->nullable(); 26 | $table->multiPolygon('multi_shapes')->default(null)->nullable(); 27 | $table->geometryCollection('multi_geometries')->default(null)->nullable(); 28 | $table->timestamps(); 29 | }); 30 | 31 | Schema::create('no_spatial_fields', function (Blueprint $table) { 32 | $table->increments('id'); 33 | $table->geometry('geometry')->default(null)->nullable(); 34 | }); 35 | 36 | Schema::create('with_srid', function (Blueprint $table) { 37 | $table->charset = 'utf8mb4'; 38 | $table->collation = 'utf8mb4_unicode_ci'; 39 | $table->increments('id'); 40 | $table->geometry('geo', 3857)->default(null)->nullable(); 41 | $table->point('location', 3857)->default(null)->nullable(); 42 | $table->lineString('line', 3857)->default(null)->nullable(); 43 | $table->polygon('shape', 3857)->default(null)->nullable(); 44 | $table->multiPoint('multi_locations', 3857)->default(null)->nullable(); 45 | $table->multiLineString('multi_lines', 3857)->default(null)->nullable(); 46 | $table->multiPolygon('multi_shapes', 3857)->default(null)->nullable(); 47 | $table->geometryCollection('multi_geometries', 3857)->default(null)->nullable(); 48 | }); 49 | } 50 | 51 | /** 52 | * Reverse the migrations. 53 | * 54 | * @return void 55 | */ 56 | public function down() 57 | { 58 | Schema::drop('geometry'); 59 | Schema::drop('no_spatial_fields'); 60 | Schema::drop('with_srid'); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Integration/Migrations/UpdateTables.php: -------------------------------------------------------------------------------- 1 | point('location')->change(); 22 | 23 | // The other field changes are just here to test if change works with them, we're not changing anything 24 | $table->geometry('geo')->default(null)->nullable()->change(); 25 | $table->lineString('line')->default(null)->nullable()->change(); 26 | $table->polygon('shape')->default(null)->nullable()->change(); 27 | $table->multiPoint('multi_locations')->default(null)->nullable()->change(); 28 | $table->multiLineString('multi_lines')->default(null)->nullable()->change(); 29 | $table->multiPolygon('multi_shapes')->default(null)->nullable()->change(); 30 | $table->geometryCollection('multi_geometries')->default(null)->nullable()->change(); 31 | 32 | // Add a spatial index on the location field 33 | $table->spatialIndex('location'); 34 | }); 35 | } 36 | 37 | /** 38 | * Reverse the migrations. 39 | * 40 | * @return void 41 | */ 42 | public function down() 43 | { 44 | Schema::table('geometry', function (Blueprint $table) { 45 | $table->dropSpatialIndex(['location']); // either an array of column names or the index name 46 | }); 47 | 48 | \DB::statement('ALTER TABLE geometry ENGINE = InnoDB'); 49 | 50 | Schema::table('geometry', function (Blueprint $table) { 51 | $table->point('location')->nullable()->change(); 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Integration/Models/GeometryModel.php: -------------------------------------------------------------------------------- 1 | geometry = new Point(1, 2); 21 | $geo->save(); 22 | 23 | $this->assertException(\Grimzy\LaravelMysqlSpatial\Exceptions\SpatialFieldsNotDefinedException::class); 24 | NoSpatialFieldsModel::all(); 25 | } 26 | 27 | public function testInsertPoint() 28 | { 29 | $geo = new GeometryModel(); 30 | $geo->location = new Point(1, 2); 31 | $geo->save(); 32 | $this->assertDatabaseHas('geometry', ['id' => $geo->id]); 33 | } 34 | 35 | public function testInsertLineString() 36 | { 37 | $geo = new GeometryModel(); 38 | 39 | $geo->location = new Point(1, 2); 40 | $geo->line = new LineString([new Point(1, 1), new Point(2, 2)]); 41 | $geo->save(); 42 | $this->assertDatabaseHas('geometry', ['id' => $geo->id]); 43 | } 44 | 45 | public function testInsertPolygon() 46 | { 47 | $geo = new GeometryModel(); 48 | 49 | $geo->location = new Point(1, 2); 50 | $geo->shape = Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))'); 51 | $geo->save(); 52 | $this->assertDatabaseHas('geometry', ['id' => $geo->id]); 53 | } 54 | 55 | public function testInsertMultiPoint() 56 | { 57 | $geo = new GeometryModel(); 58 | 59 | $geo->location = new Point(1, 2); 60 | $geo->multi_locations = new MultiPoint([new Point(1, 1), new Point(2, 2)]); 61 | $geo->save(); 62 | $this->assertDatabaseHas('geometry', ['id' => $geo->id]); 63 | } 64 | 65 | public function testInsertMultiPolygon() 66 | { 67 | $geo = new GeometryModel(); 68 | 69 | $geo->location = new Point(1, 2); 70 | 71 | $geo->multi_shapes = new MultiPolygon([ 72 | Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))'), 73 | Polygon::fromWKT('POLYGON((0 0,0 5,5 5,5 0,0 0))'), 74 | ]); 75 | $geo->save(); 76 | $this->assertDatabaseHas('geometry', ['id' => $geo->id]); 77 | } 78 | 79 | public function testInsertGeometryCollection() 80 | { 81 | $geo = new GeometryModel(); 82 | 83 | $geo->location = new Point(1, 2); 84 | 85 | $geo->multi_geometries = new GeometryCollection([ 86 | Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))'), 87 | Polygon::fromWKT('POLYGON((0 0,0 5,5 5,5 0,0 0))'), 88 | new Point(0, 0), 89 | ]); 90 | $geo->save(); 91 | $this->assertDatabaseHas('geometry', ['id' => $geo->id]); 92 | } 93 | 94 | public function testInsertEmptyGeometryCollection() 95 | { 96 | $geo = new GeometryModel(); 97 | 98 | $geo->location = new Point(1, 2); 99 | 100 | $geo->multi_geometries = new GeometryCollection([]); 101 | $geo->save(); 102 | $this->assertDatabaseHas('geometry', ['id' => $geo->id]); 103 | 104 | $geo2 = GeometryModel::find($geo->id); 105 | $this->assertInstanceOf(GeometryCollection::class, $geo2->multi_geometries); 106 | $this->assertEquals(0, count($geo2->multi_geometries)); 107 | } 108 | 109 | public function testUpdate() 110 | { 111 | $geo = new GeometryModel(); 112 | $geo->location = new Point(1, 2); 113 | $geo->save(); 114 | 115 | $to_update = GeometryModel::all()->first(); 116 | $to_update->location = new Point(2, 3); 117 | $to_update->save(); 118 | 119 | $this->assertDatabaseHas('geometry', ['id' => $to_update->id]); 120 | 121 | $all = GeometryModel::all(); 122 | $this->assertCount(1, $all); 123 | 124 | $updated = $all->first(); 125 | $this->assertInstanceOf(Point::class, $updated->location); 126 | $this->assertEquals(2, $updated->location->getLat()); 127 | $this->assertEquals(3, $updated->location->getLng()); 128 | } 129 | 130 | public function testDistance() 131 | { 132 | $loc1 = new GeometryModel(); 133 | $loc1->location = new Point(1, 1); 134 | $loc1->save(); 135 | 136 | $loc2 = new GeometryModel(); 137 | $loc2->location = new Point(2, 2); // Distance from loc1: 1.4142135623731 138 | $loc2->save(); 139 | 140 | $loc3 = new GeometryModel(); 141 | $loc3->location = new Point(3, 3); // Distance from loc1: 2.8284271247462 142 | $loc3->save(); 143 | 144 | $a = GeometryModel::distance('location', $loc1->location, 2)->get(); 145 | $this->assertCount(2, $a); 146 | $this->assertTrue($a->contains('location', $loc1->location)); 147 | $this->assertTrue($a->contains('location', $loc2->location)); 148 | $this->assertFalse($a->contains('location', $loc3->location)); 149 | 150 | // Excluding self 151 | $b = GeometryModel::distanceExcludingSelf('location', $loc1->location, 2)->get(); 152 | $this->assertCount(1, $b); 153 | $this->assertFalse($b->contains('location', $loc1->location)); 154 | $this->assertTrue($b->contains('location', $loc2->location)); 155 | $this->assertFalse($b->contains('location', $loc3->location)); 156 | 157 | $c = GeometryModel::distance('location', $loc1->location, 1)->get(); 158 | $this->assertCount(1, $c); 159 | $this->assertTrue($c->contains('location', $loc1->location)); 160 | $this->assertFalse($c->contains('location', $loc2->location)); 161 | $this->assertFalse($c->contains('location', $loc3->location)); 162 | } 163 | 164 | public function testDistanceSphere() 165 | { 166 | $loc1 = new GeometryModel(); 167 | $loc1->location = new Point(40.767864, -73.971732); 168 | $loc1->save(); 169 | 170 | $loc2 = new GeometryModel(); 171 | $loc2->location = new Point(40.767664, -73.971271); // Distance from loc1: 44.741406484588 172 | $loc2->save(); 173 | 174 | $loc3 = new GeometryModel(); 175 | $loc3->location = new Point(40.761434, -73.977619); // Distance from loc1: 870.06424066202 176 | $loc3->save(); 177 | 178 | $a = GeometryModel::distanceSphere('location', $loc1->location, 200)->get(); 179 | $this->assertCount(2, $a); 180 | $this->assertTrue($a->contains('location', $loc1->location)); 181 | $this->assertTrue($a->contains('location', $loc2->location)); 182 | $this->assertFalse($a->contains('location', $loc3->location)); 183 | 184 | // Excluding self 185 | $b = GeometryModel::distanceSphereExcludingSelf('location', $loc1->location, 200)->get(); 186 | $this->assertCount(1, $b); 187 | $this->assertFalse($b->contains('location', $loc1->location)); 188 | $this->assertTrue($b->contains('location', $loc2->location)); 189 | $this->assertFalse($b->contains('location', $loc3->location)); 190 | 191 | if ($this->after_fix) { 192 | $c = GeometryModel::distanceSphere('location', $loc1->location, 44.741406484236)->get(); 193 | } else { 194 | $c = GeometryModel::distanceSphere('location', $loc1->location, 44.741406484587)->get(); 195 | } 196 | $this->assertCount(1, $c); 197 | $this->assertTrue($c->contains('location', $loc1->location)); 198 | $this->assertFalse($c->contains('location', $loc2->location)); 199 | $this->assertFalse($c->contains('location', $loc3->location)); 200 | } 201 | 202 | public function testDistanceValue() 203 | { 204 | $loc1 = new GeometryModel(); 205 | $loc1->location = new Point(1, 1); 206 | $loc1->save(); 207 | 208 | $loc2 = new GeometryModel(); 209 | $loc2->location = new Point(2, 2); // Distance from loc1: 1.4142135623731 210 | $loc2->save(); 211 | 212 | $a = GeometryModel::distanceValue('location', $loc1->location)->get(); 213 | $this->assertCount(2, $a); 214 | $this->assertEquals(0, $a[0]->distance); 215 | $this->assertEquals(1.4142135623, $a[1]->distance); // PHP floats' 11th+ digits don't matter 216 | } 217 | 218 | public function testDistanceSphereValue() 219 | { 220 | $loc1 = new GeometryModel(); 221 | $loc1->location = new Point(40.767864, -73.971732); 222 | $loc1->save(); 223 | 224 | $loc2 = new GeometryModel(); 225 | $loc2->location = new Point(40.767664, -73.971271); // Distance from loc1: 44.741406484236 226 | $loc2->save(); 227 | 228 | $a = GeometryModel::distanceSphereValue('location', $loc1->location)->get(); 229 | $this->assertCount(2, $a); 230 | $this->assertEquals(0, $a[0]->distance); 231 | 232 | if ($this->after_fix) { 233 | $this->assertEquals(44.7414064842, $a[1]->distance); // PHP floats' 11th+ digits don't matter 234 | } else { 235 | $this->assertEquals(44.7414064845, $a[1]->distance); // PHP floats' 11th+ digits don't matter 236 | } 237 | } 238 | 239 | public function testOrderBySpatialWithUnknownFunction() 240 | { 241 | $loc = new GeometryModel(); 242 | $loc->location = new Point(1, 1); 243 | 244 | $this->assertException(\Grimzy\LaravelMysqlSpatial\Exceptions\UnknownSpatialFunctionException::class); 245 | GeometryModel::orderBySpatial('location', $loc->location, 'does-not-exist')->get(); 246 | } 247 | 248 | public function testOrderByDistance() 249 | { 250 | $loc2 = new GeometryModel(); 251 | $loc2->location = new Point(2, 2); // Distance from loc1: 1.4142135623731 252 | $loc2->save(); 253 | 254 | $loc1 = new GeometryModel(); 255 | $loc1->location = new Point(1, 1); 256 | $loc1->save(); 257 | 258 | $loc3 = new GeometryModel(); 259 | $loc3->location = new Point(3, 3); // Distance from loc1: 2.8284271247462 260 | $loc3->save(); 261 | 262 | $a = GeometryModel::orderByDistance('location', $loc1->location)->get(); 263 | $this->assertCount(3, $a); 264 | $this->assertEquals($loc1->location, $a[0]->location); 265 | $this->assertEquals($loc2->location, $a[1]->location); 266 | $this->assertEquals($loc3->location, $a[2]->location); 267 | 268 | // Excluding self 269 | $b = GeometryModel::orderByDistance('location', $loc1->location, 'asc')->get(); 270 | $this->assertCount(3, $b); 271 | $this->assertEquals($loc1->location, $b[0]->location); 272 | $this->assertEquals($loc2->location, $b[1]->location); 273 | $this->assertEquals($loc3->location, $b[2]->location); 274 | 275 | $c = GeometryModel::orderByDistance('location', $loc1->location, 'desc')->get(); 276 | $this->assertCount(3, $c); 277 | $this->assertEquals($loc3->location, $c[0]->location); 278 | $this->assertEquals($loc2->location, $c[1]->location); 279 | $this->assertEquals($loc1->location, $c[2]->location); 280 | } 281 | 282 | public function testOrderByDistanceSphere() 283 | { 284 | $loc2 = new GeometryModel(); 285 | $loc2->location = new Point(40.767664, -73.971271); // Distance from loc1: 44.741406484588 286 | $loc2->save(); 287 | 288 | $loc1 = new GeometryModel(); 289 | $loc1->location = new Point(40.767864, -73.971732); 290 | $loc1->save(); 291 | 292 | $loc3 = new GeometryModel(); 293 | $loc3->location = new Point(40.761434, -73.977619); // Distance from loc1: 870.06424066202 294 | $loc3->save(); 295 | 296 | $a = GeometryModel::orderByDistanceSphere('location', $loc1->location)->get(); 297 | $this->assertCount(3, $a); 298 | $this->assertEquals($loc1->location, $a[0]->location); 299 | $this->assertEquals($loc2->location, $a[1]->location); 300 | $this->assertEquals($loc3->location, $a[2]->location); 301 | 302 | $b = GeometryModel::orderByDistanceSphere('location', $loc1->location, 'asc')->get(); 303 | $this->assertCount(3, $b); 304 | $this->assertEquals($loc1->location, $b[0]->location); 305 | $this->assertEquals($loc2->location, $b[1]->location); 306 | $this->assertEquals($loc3->location, $b[2]->location); 307 | 308 | $c = GeometryModel::orderByDistanceSphere('location', $loc1->location, 'desc')->get(); 309 | $this->assertCount(3, $c); 310 | $this->assertEquals($loc3->location, $c[0]->location); 311 | $this->assertEquals($loc2->location, $c[1]->location); 312 | $this->assertEquals($loc1->location, $c[2]->location); 313 | } 314 | 315 | //public function testBounding() { 316 | // $point = new Point(0, 0); 317 | // 318 | // $linestring1 = \Grimzy\LaravelMysqlSpatial\Types\LineString::fromWkt("LINESTRING(1 1, 2 2)"); 319 | // $linestring2 = \Grimzy\LaravelMysqlSpatial\Types\LineString::fromWkt("LINESTRING(20 20, 24 24)"); 320 | // $linestring3 = \Grimzy\LaravelMysqlSpatial\Types\LineString::fromWkt("LINESTRING(0 10, 10 10)"); 321 | // 322 | // $geo1 = new GeometryModel(); 323 | // $geo1->location = $point; 324 | // $geo1->line = $linestring1; 325 | // $geo1->save(); 326 | // 327 | // $geo2 = new GeometryModel(); 328 | // $geo2->location = $point; 329 | // $geo2->line = $linestring2; 330 | // $geo2->save(); 331 | // 332 | // $geo3 = new GeometryModel(); 333 | // $geo3->location = $point; 334 | // $geo3->line = $linestring3; 335 | // $geo3->save(); 336 | // 337 | // $polygon = Polygon::fromWKT("POLYGON((0 10,10 10,10 0,0 0,0 10))"); 338 | // 339 | // $result = GeometryModel::Bounding($polygon, 'line')->get(); 340 | // $this->assertCount(2, $result); 341 | // $this->assertTrue($result->contains($geo1)); 342 | // $this->assertFalse($result->contains($geo2)); 343 | // $this->assertTrue($result->contains($geo3)); 344 | // 345 | //} 346 | } 347 | -------------------------------------------------------------------------------- /tests/Integration/SridSpatialTest.php: -------------------------------------------------------------------------------- 1 | location = new Point(1, 2, 3857); 21 | $geo->save(); 22 | $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); 23 | } 24 | 25 | public function testInsertLineStringWithSrid() 26 | { 27 | $geo = new WithSridModel(); 28 | 29 | $geo->location = new Point(1, 2, 3857); 30 | $geo->line = new LineString([new Point(1, 1), new Point(2, 2)], 3857); 31 | $geo->save(); 32 | $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); 33 | } 34 | 35 | public function testInsertPolygonWithSrid() 36 | { 37 | $geo = new WithSridModel(); 38 | 39 | $geo->location = new Point(1, 2, 3857); 40 | $geo->shape = Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))', 3857); 41 | $geo->save(); 42 | $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); 43 | } 44 | 45 | public function testInsertMultiPointWithSrid() 46 | { 47 | $geo = new WithSridModel(); 48 | 49 | $geo->location = new Point(1, 2, 3857); 50 | $geo->multi_locations = new MultiPoint([new Point(1, 1), new Point(2, 2)], 3857); 51 | $geo->save(); 52 | $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); 53 | } 54 | 55 | public function testInsertMultiPolygonWithSrid() 56 | { 57 | $geo = new WithSridModel(); 58 | 59 | $geo->location = new Point(1, 2, 3857); 60 | 61 | $geo->multi_shapes = new MultiPolygon([ 62 | Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))'), 63 | Polygon::fromWKT('POLYGON((0 0,0 5,5 5,5 0,0 0))'), 64 | ], 3857); 65 | $geo->save(); 66 | $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); 67 | } 68 | 69 | public function testInsertGeometryCollectionWithSrid() 70 | { 71 | $geo = new WithSridModel(); 72 | 73 | $geo->location = new Point(1, 2, 3857); 74 | 75 | $geo->multi_geometries = new GeometryCollection([ 76 | Polygon::fromWKT('POLYGON((0 10,10 10,10 0,0 0,0 10))'), 77 | Polygon::fromWKT('POLYGON((0 0,0 5,5 5,5 0,0 0))'), 78 | new Point(0, 0), 79 | ], 3857); 80 | $geo->save(); 81 | $this->assertDatabaseHas('with_srid', ['id' => $geo->id]); 82 | } 83 | 84 | public function testUpdateWithSrid() 85 | { 86 | $geo = new WithSridModel(); 87 | $geo->location = new Point(1, 2, 3857); 88 | $geo->save(); 89 | 90 | $to_update = WithSridModel::all()->first(); 91 | $to_update->location = new Point(2, 3, 3857); 92 | $to_update->save(); 93 | 94 | $this->assertDatabaseHas('with_srid', ['id' => $to_update->id]); 95 | 96 | $all = WithSridModel::all(); 97 | $this->assertCount(1, $all); 98 | 99 | $updated = $all->first(); 100 | $this->assertInstanceOf(Point::class, $updated->location); 101 | $this->assertEquals(2, $updated->location->getLat()); 102 | $this->assertEquals(3, $updated->location->getLng()); 103 | } 104 | 105 | public function testInsertPointWithWrongSrid() 106 | { 107 | $geo = new WithSridModel(); 108 | $geo->location = new Point(1, 2); 109 | 110 | $this->assertException( 111 | Illuminate\Database\QueryException::class, 112 | 'SQLSTATE[HY000]: General error: 3643 The SRID of the geometry '. 113 | 'does not match the SRID of the column \'location\'. The SRID '. 114 | 'of the geometry is 0, but the SRID of the column is 3857. '. 115 | 'Consider changing the SRID of the geometry or the SRID property '. 116 | 'of the column. (SQL: insert into `with_srid` (`location`) values '. 117 | '(ST_GeomFromText(POINT(2 1), 0, \'axis-order=long-lat\')))' 118 | ); 119 | $geo->save(); 120 | } 121 | 122 | public function testGeometryInsertedHasRightSrid() 123 | { 124 | $geo = new WithSridModel(); 125 | $geo->location = new Point(1, 2, 3857); 126 | $geo->save(); 127 | 128 | $srid = \DB::selectOne('select ST_SRID(location) as srid from with_srid'); 129 | $this->assertEquals(3857, $srid->srid); 130 | 131 | $result = WithSridModel::first(); 132 | 133 | $this->assertEquals($geo->location->getSrid(), $result->location->getSrid()); 134 | $a = 1; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/Unit/BaseTestCase.php: -------------------------------------------------------------------------------- 1 | setExpectedException($exceptionName, $exceptionMessage, $exceptionCode); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Unit/Connectors/ConnectionFactoryTest.php: -------------------------------------------------------------------------------- 1 | makePartial(); 15 | $factory->shouldAllowMockingProtectedMethods(); 16 | $conn = $factory->createConnection('mysql', $pdo, 'database'); 17 | 18 | $this->assertInstanceOf(MysqlConnection::class, $conn); 19 | } 20 | 21 | public function testCreateConnectionDifferentDriver() 22 | { 23 | $pdo = new PDOStub(); 24 | 25 | $factory = Mockery::mock(ConnectionFactory::class, [new Container()])->makePartial(); 26 | $factory->shouldAllowMockingProtectedMethods(); 27 | $conn = $factory->createConnection('pgsql', $pdo, 'database'); 28 | 29 | $this->assertInstanceOf(\Illuminate\Database\PostgresConnection::class, $conn); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Eloquent/BuilderTest.php: -------------------------------------------------------------------------------- 1 | makePartial(); 26 | $grammar = Mockery::mock(MySqlGrammar::class)->makePartial(); 27 | $this->queryBuilder = Mockery::mock(QueryBuilder::class, [$connection, $grammar]); 28 | 29 | $this->queryBuilder 30 | ->shouldReceive('from') 31 | ->once() 32 | ->andReturn($this->queryBuilder); 33 | 34 | $this->builder = new Builder($this->queryBuilder); 35 | $this->builder->setModel(new TestBuilderModel()); 36 | } 37 | 38 | public function testUpdatePoint() 39 | { 40 | $point = new Point(1, 2); 41 | $this->queryBuilder 42 | ->shouldReceive('update') 43 | ->with(['point' => new SpatialExpression($point)]) 44 | ->once() 45 | ->andReturn(1); 46 | 47 | $result = $this->builder->update(['point' => $point]); 48 | 49 | $this->assertSame(1, $result); 50 | } 51 | 52 | public function testUpdateLinestring() 53 | { 54 | $linestring = new LineString([new Point(0, 0), new Point(1, 1), new Point(2, 2)]); 55 | 56 | $this->queryBuilder 57 | ->shouldReceive('update') 58 | ->with(['linestring' => new SpatialExpression($linestring)]) 59 | ->once() 60 | ->andReturn(1); 61 | 62 | $result = $this->builder->update(['linestring' => $linestring]); 63 | 64 | $this->assertSame(1, $result); 65 | } 66 | 67 | public function testUpdatePolygon() 68 | { 69 | $linestrings[] = new LineString([new Point(0, 0), new Point(0, 1)]); 70 | $linestrings[] = new LineString([new Point(0, 1), new Point(1, 1)]); 71 | $linestrings[] = new LineString([new Point(1, 1), new Point(0, 0)]); 72 | $polygon = new Polygon($linestrings); 73 | 74 | $this->queryBuilder 75 | ->shouldReceive('update') 76 | ->with(['polygon' => new SpatialExpression($polygon)]) 77 | ->once() 78 | ->andReturn(1); 79 | 80 | $result = $this->builder->update(['polygon' => $polygon]); 81 | 82 | $this->assertSame(1, $result); 83 | } 84 | 85 | public function testUpdatePointWithSrid() 86 | { 87 | $point = new Point(1, 2, 4326); 88 | $this->queryBuilder 89 | ->shouldReceive('update') 90 | ->with(['point' => new SpatialExpression($point)]) 91 | ->once() 92 | ->andReturn(1); 93 | 94 | $result = $this->builder->update(['point' => $point]); 95 | 96 | $this->assertSame(1, $result); 97 | } 98 | 99 | public function testUpdateLinestringWithSrid() 100 | { 101 | $linestring = new LineString([new Point(0, 0), new Point(1, 1), new Point(2, 2)], 4326); 102 | 103 | $this->queryBuilder 104 | ->shouldReceive('update') 105 | ->with(['linestring' => new SpatialExpression($linestring)]) 106 | ->once() 107 | ->andReturn(1); 108 | 109 | $result = $this->builder->update(['linestring' => $linestring]); 110 | 111 | $this->assertSame(1, $result); 112 | } 113 | 114 | public function testUpdatePolygonWithSrid() 115 | { 116 | $linestrings[] = new LineString([new Point(0, 0), new Point(0, 1)]); 117 | $linestrings[] = new LineString([new Point(0, 1), new Point(1, 1)]); 118 | $linestrings[] = new LineString([new Point(1, 1), new Point(0, 0)]); 119 | $polygon = new Polygon($linestrings, 4326); 120 | 121 | $this->queryBuilder 122 | ->shouldReceive('update') 123 | ->with(['polygon' => new SpatialExpression($polygon)]) 124 | ->once() 125 | ->andReturn(1); 126 | 127 | $result = $this->builder->update(['polygon' => $polygon]); 128 | 129 | $this->assertSame(1, $result); 130 | } 131 | } 132 | 133 | class TestBuilderModel extends Model 134 | { 135 | use SpatialTrait; 136 | 137 | public $timestamps = false; 138 | protected $spatialFields = ['point', 'linestring', 'polygon']; 139 | } 140 | -------------------------------------------------------------------------------- /tests/Unit/Eloquent/SpatialTraitTest.php: -------------------------------------------------------------------------------- 1 | model = new TestModel(); 27 | $this->queries = &$this->model->getConnection()->getPdo()->queries; 28 | } 29 | 30 | public function tearDown() 31 | { 32 | $this->model->getConnection()->getPdo()->resetQueries(); 33 | } 34 | 35 | public function testInsertUpdatePointHasCorrectSql() 36 | { 37 | $this->assertFalse($this->model->exists); 38 | 39 | $this->model->point = new Point(1, 2); 40 | $this->model->save(); 41 | 42 | $this->assertStringStartsWith('insert', $this->queries[0]); 43 | $this->assertContains('insert into `test_models` (`point`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); 44 | // TODO: assert bindings in query 45 | $this->assertTrue($this->model->exists); 46 | 47 | $this->model->point = new Point(1, 2); 48 | $this->model->save(); 49 | 50 | $this->assertStringStartsWith('update', $this->queries[1]); 51 | $this->assertContains('update `test_models` set `point` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); 52 | // TODO: assert bindings in query 53 | } 54 | 55 | public function testInsertUpdateLineStringHasCorrectSql() 56 | { 57 | $point1 = new Point(1, 2); 58 | $point2 = new Point(2, 3); 59 | 60 | $this->assertFalse($this->model->exists); 61 | 62 | $this->model->linestring = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point1, $point2]); 63 | $this->model->save(); 64 | 65 | $this->assertStringStartsWith('insert', $this->queries[0]); 66 | $this->assertContains('insert into `test_models` (`linestring`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); 67 | // TODO: assert bindings in query 68 | $this->assertTrue($this->model->exists); 69 | 70 | $this->model->linestring = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point1, $point2]); 71 | $this->model->save(); 72 | 73 | $this->assertStringStartsWith('update', $this->queries[1]); 74 | $this->assertContains('update `test_models` set `linestring` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); 75 | // TODO: assert bindings in query 76 | } 77 | 78 | public function testInsertUpdatePolygonHasCorrectSql() 79 | { 80 | $point1 = new Point(1, 2); 81 | $point2 = new Point(2, 3); 82 | $linestring1 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point1, $point2]); 83 | $point3 = new Point(3, 2); 84 | $point4 = new Point(2, 1); 85 | $linestring2 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point3, $point4]); 86 | 87 | $this->assertFalse($this->model->exists); 88 | 89 | $this->model->polygon = new \Grimzy\LaravelMysqlSpatial\Types\Polygon([$linestring1, $linestring2]); 90 | $this->model->save(); 91 | 92 | $this->assertStringStartsWith('insert', $this->queries[0]); 93 | $this->assertContains('insert into `test_models` (`polygon`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); 94 | // TODO: assert bindings in query 95 | $this->assertTrue($this->model->exists); 96 | 97 | $this->model->polygon = new \Grimzy\LaravelMysqlSpatial\Types\Polygon([$linestring1, $linestring2]); 98 | $this->model->save(); 99 | $this->assertStringStartsWith('update', $this->queries[1]); 100 | $this->assertContains('update `test_models` set `polygon` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); 101 | // TODO: assert bindings in query 102 | } 103 | 104 | public function testInsertUpdateMultiPointHasCorrectSql() 105 | { 106 | $point1 = new Point(1, 2); 107 | $point2 = new Point(2, 3); 108 | 109 | $this->assertFalse($this->model->exists); 110 | 111 | $this->model->multipoint = new \Grimzy\LaravelMysqlSpatial\Types\MultiPoint([$point1, $point2]); 112 | $this->model->save(); 113 | 114 | $this->assertStringStartsWith('insert', $this->queries[0]); 115 | $this->assertContains('insert into `test_models` (`multipoint`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); 116 | // TODO: assert bindings in query 117 | $this->assertTrue($this->model->exists); 118 | 119 | $this->model->multipoint = new \Grimzy\LaravelMysqlSpatial\Types\MultiPoint([$point1, $point2]); 120 | $this->model->save(); 121 | 122 | $this->assertStringStartsWith('update', $this->queries[1]); 123 | $this->assertContains('update `test_models` set `multipoint` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); 124 | // TODO: assert bindings in query 125 | } 126 | 127 | public function testInsertUpdateMultiLineStringHasCorrectSql() 128 | { 129 | $point1 = new Point(1, 2); 130 | $point2 = new Point(2, 3); 131 | $linestring1 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point1, $point2]); 132 | $point3 = new Point(3, 2); 133 | $point4 = new Point(2, 1); 134 | $linestring2 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point3, $point4]); 135 | 136 | $this->assertFalse($this->model->exists); 137 | 138 | $this->model->multilinestring = new \Grimzy\LaravelMysqlSpatial\Types\MultiLineString([$linestring1, $linestring2]); 139 | $this->model->save(); 140 | 141 | $this->assertStringStartsWith('insert', $this->queries[0]); 142 | $this->assertContains('insert into `test_models` (`multilinestring`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); 143 | // TODO: assert bindings in query 144 | $this->assertTrue($this->model->exists); 145 | 146 | $this->model->multilinestring = new \Grimzy\LaravelMysqlSpatial\Types\MultiLineString([$linestring1, $linestring2]); 147 | $this->model->save(); 148 | $this->assertStringStartsWith('update', $this->queries[1]); 149 | $this->assertContains('update `test_models` set `multilinestring` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); 150 | // TODO: assert bindings in query 151 | } 152 | 153 | public function testInsertUpdateMultiPolygonHasCorrectSql() 154 | { 155 | $point1 = new Point(1, 2); 156 | $point2 = new Point(2, 3); 157 | $linestring1 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point1, $point2]); 158 | $point3 = new Point(3, 2); 159 | $point4 = new Point(2, 1); 160 | $linestring2 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point3, $point4]); 161 | $polygon1 = new \Grimzy\LaravelMysqlSpatial\Types\Polygon([$linestring1, $linestring2]); 162 | 163 | $point5 = new Point(4, 5); 164 | $point6 = new Point(5, 6); 165 | $linestring3 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point5, $point6]); 166 | $point7 = new Point(6, 5); 167 | $point8 = new Point(5, 4); 168 | $linestring4 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point7, $point8]); 169 | $polygon2 = new \Grimzy\LaravelMysqlSpatial\Types\Polygon([$linestring3, $linestring4]); 170 | 171 | $this->assertFalse($this->model->exists); 172 | 173 | $this->model->multipolygon = new \Grimzy\LaravelMysqlSpatial\Types\MultiPolygon([$polygon1, $polygon2]); 174 | $this->model->save(); 175 | 176 | $this->assertStringStartsWith('insert', $this->queries[0]); 177 | $this->assertContains('insert into `test_models` (`multipolygon`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); 178 | // TODO: assert bindings in query 179 | $this->assertTrue($this->model->exists); 180 | 181 | $this->model->multipolygon = new \Grimzy\LaravelMysqlSpatial\Types\MultiPolygon([$polygon1, $polygon2]); 182 | $this->model->save(); 183 | $this->assertStringStartsWith('update', $this->queries[1]); 184 | $this->assertContains('update `test_models` set `multipolygon` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); 185 | // TODO: assert bindings in query 186 | } 187 | 188 | public function testInsertUpdateGeometryCollectionHasCorrectSql() 189 | { 190 | $point1 = new Point(1, 2); 191 | $point2 = new Point(2, 3); 192 | $point3 = new Point(3, 3); 193 | $linestring1 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point2, $point3]); 194 | 195 | $this->assertFalse($this->model->exists); 196 | 197 | $this->model->geometrycollection = new \Grimzy\LaravelMysqlSpatial\Types\GeometryCollection([$point1, $linestring1]); 198 | $this->model->save(); 199 | 200 | $this->assertStringStartsWith('insert', $this->queries[0]); 201 | $this->assertContains('insert into `test_models` (`geometrycollection`) values (ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $this->queries[0]); 202 | // TODO: assert bindings in query 203 | $this->assertTrue($this->model->exists); 204 | 205 | $this->model->geometrycollection = new \Grimzy\LaravelMysqlSpatial\Types\GeometryCollection([$point1, $linestring1]); 206 | $this->model->save(); 207 | $this->assertStringStartsWith('update', $this->queries[1]); 208 | $this->assertContains('update `test_models` set `geometrycollection` = ST_GeomFromText(?, ?, \'axis-order=long-lat\') where `id` = ?', $this->queries[1]); 209 | // TODO: assert bindings in query 210 | } 211 | 212 | public function testSettingRawAttributes() 213 | { 214 | $attributes['point'] = "\0\0\0\0".'0101000000000000000000f03f0000000000000040'; 215 | 216 | $this->model->setRawAttributes($attributes); 217 | $this->assertInstanceOf(Point::class, ($this->model->point)); 218 | } 219 | 220 | public function testSpatialFieldsNotDefinedException() 221 | { 222 | $model = new TestNoSpatialModel(); 223 | $this->assertException( 224 | SpatialFieldsNotDefinedException::class, 225 | 'TestNoSpatialModel has to define $spatialFields' 226 | ); 227 | $model->getSpatialFields(); 228 | } 229 | 230 | public function testScopeDistance() 231 | { 232 | $point = new Point(1, 2); 233 | $query = TestModel::distance('point', $point, 10); 234 | 235 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 236 | $q = $query->getQuery(); 237 | $this->assertNotEmpty($q->wheres); 238 | $bindings = $q->getRawBindings()['where']; 239 | $this->assertNotEmpty($bindings); 240 | $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); 241 | $this->assertEquals('POINT(2 1)', $bindings[0]); 242 | $this->assertEquals(10, $bindings[2]); 243 | } 244 | 245 | public function testScopeDistanceExcludingSelf() 246 | { 247 | $point = new Point(1, 2); 248 | $query = TestModel::distanceExcludingSelf('point', $point, 10); 249 | 250 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 251 | $q = $query->getQuery(); 252 | $this->assertNotEmpty($q->wheres); 253 | $bindings = $q->getRawBindings()['where']; 254 | $this->assertNotEmpty($bindings); 255 | $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); 256 | $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) != 0', $q->wheres[1]['sql']); 257 | $this->assertEquals('POINT(2 1)', $bindings[0]); 258 | $this->assertEquals(10, $bindings[2]); 259 | $this->assertEquals('POINT(2 1)', $bindings[3]); 260 | } 261 | 262 | public function testScopeDistanceSphere() 263 | { 264 | $point = new Point(1, 2); 265 | $query = TestModel::distanceSphere('point', $point, 10); 266 | 267 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 268 | $q = $query->getQuery(); 269 | $this->assertNotEmpty($q->wheres); 270 | $bindings = $q->getRawBindings()['where']; 271 | $this->assertNotEmpty($bindings); 272 | $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); 273 | $this->assertEquals('POINT(2 1)', $bindings[0]); 274 | $this->assertEquals(10, $bindings[2]); 275 | } 276 | 277 | public function testScopeDistanceSphereExcludingSelf() 278 | { 279 | $point = new Point(1, 2); 280 | $query = TestModel::distanceSphereExcludingSelf('point', $point, 10); 281 | 282 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 283 | $q = $query->getQuery(); 284 | $this->assertNotEmpty($q->wheres); 285 | $bindings = $q->getRawBindings()['where']; 286 | $this->assertNotEmpty($bindings); 287 | $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) <= ?', $q->wheres[0]['sql']); 288 | $this->assertEquals('st_distance_sphere(point, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) != 0', $q->wheres[1]['sql']); 289 | $this->assertEquals('POINT(2 1)', $bindings[0]); 290 | $this->assertEquals(10, $bindings[2]); 291 | $this->assertEquals('POINT(2 1)', $bindings[3]); 292 | } 293 | 294 | public function testScopeDistanceValue() 295 | { 296 | $point = new Point(1, 2); 297 | $query = TestModel::distanceValue('point', $point); 298 | 299 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 300 | $q = $query->getQuery(); 301 | $this->assertNotEmpty($q->columns); 302 | $bindings = $q->getRawBindings()['select']; 303 | $this->assertNotEmpty($bindings); 304 | $this->assertEquals('*', $q->columns[0]); 305 | $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); 306 | $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); 307 | $this->assertEquals('POINT(2 1)', $bindings[0]); 308 | } 309 | 310 | public function testScopeDistanceValueWithSelect() 311 | { 312 | $point = new Point(1, 2); 313 | $query = TestModel::select('some_column')->distanceValue('point', $point); 314 | 315 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 316 | $q = $query->getQuery(); 317 | $this->assertNotEmpty($q->columns); 318 | $bindings = $q->getRawBindings()['select']; 319 | $this->assertNotEmpty($bindings); 320 | $this->assertEquals('some_column', $q->columns[0]); 321 | $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); 322 | $this->assertEquals('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); 323 | $this->assertEquals('POINT(2 1)', $bindings[0]); 324 | } 325 | 326 | public function testScopeDistanceSphereValue() 327 | { 328 | $point = new Point(1, 2); 329 | $query = TestModel::distanceSphereValue('point', $point); 330 | 331 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 332 | $q = $query->getQuery(); 333 | $this->assertNotEmpty($q->columns); 334 | $bindings = $q->getRawBindings()['select']; 335 | $this->assertNotEmpty($bindings); 336 | $this->assertEquals('*', $q->columns[0]); 337 | $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); 338 | $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); 339 | $this->assertEquals('POINT(2 1)', $bindings[0]); 340 | } 341 | 342 | public function testScopeDistanceSphereValueWithSelect() 343 | { 344 | $point = new Point(1, 2); 345 | $query = TestModel::select('some_column')->distanceSphereValue('point', $point); 346 | 347 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 348 | $q = $query->getQuery(); 349 | $this->assertNotEmpty($q->columns); 350 | $bindings = $q->getRawBindings()['select']; 351 | $this->assertNotEmpty($bindings); 352 | $this->assertEquals('some_column', $q->columns[0]); 353 | $this->assertInstanceOf(\Illuminate\Database\Query\Expression::class, $q->columns[1]); 354 | $this->assertEquals('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) as distance', $q->columns[1]->getValue()); 355 | $this->assertEquals('POINT(2 1)', $bindings[0]); 356 | } 357 | 358 | private function buildTestPolygon() 359 | { 360 | $point1 = new Point(1, 1); 361 | $point2 = new Point(1, 2); 362 | $linestring1 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point1, $point2]); 363 | $point3 = new Point(1, 2); 364 | $point4 = new Point(2, 2); 365 | $linestring2 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point3, $point4]); 366 | $point5 = new Point(2, 2); 367 | $point6 = new Point(1, 1); 368 | $linestring3 = new \Grimzy\LaravelMysqlSpatial\Types\LineString([$point5, $point6]); 369 | 370 | return new \Grimzy\LaravelMysqlSpatial\Types\Polygon([$linestring1, $linestring2, $linestring3]); 371 | } 372 | 373 | public function testScopeComparison() 374 | { 375 | $query = TestModel::comparison('point', $this->buildTestPolygon(), 'within'); 376 | 377 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 378 | $q = $query->getQuery(); 379 | $this->assertNotEmpty($q->wheres); 380 | $bindings = $q->getRawBindings()['where']; 381 | $this->assertNotEmpty($bindings); 382 | $this->assertContains('st_within(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 383 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 384 | } 385 | 386 | public function testScopeWithin() 387 | { 388 | $query = TestModel::within('point', $this->buildTestPolygon()); 389 | 390 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 391 | $q = $query->getQuery(); 392 | $this->assertNotEmpty($q->wheres); 393 | $bindings = $q->getRawBindings()['where']; 394 | $this->assertNotEmpty($bindings); 395 | $this->assertContains('st_within(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 396 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 397 | } 398 | 399 | public function testScopeCrosses() 400 | { 401 | $query = TestModel::crosses('point', $this->buildTestPolygon()); 402 | 403 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 404 | $q = $query->getQuery(); 405 | $this->assertNotEmpty($q->wheres); 406 | $bindings = $q->getRawBindings()['where']; 407 | $this->assertNotEmpty($bindings); 408 | $this->assertContains('st_crosses(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 409 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 410 | } 411 | 412 | public function testScopeContains() 413 | { 414 | $query = TestModel::contains('point', $this->buildTestPolygon()); 415 | 416 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 417 | $q = $query->getQuery(); 418 | $this->assertNotEmpty($q->wheres); 419 | $bindings = $q->getRawBindings()['where']; 420 | $this->assertNotEmpty($bindings); 421 | $this->assertContains('st_contains(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 422 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 423 | } 424 | 425 | public function testScopeDisjoint() 426 | { 427 | $query = TestModel::disjoint('point', $this->buildTestPolygon()); 428 | 429 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 430 | $q = $query->getQuery(); 431 | $this->assertNotEmpty($q->wheres); 432 | $bindings = $q->getRawBindings()['where']; 433 | $this->assertNotEmpty($bindings); 434 | $this->assertContains('st_disjoint(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 435 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 436 | } 437 | 438 | public function testScopeEquals() 439 | { 440 | $query = TestModel::equals('point', $this->buildTestPolygon()); 441 | 442 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 443 | $q = $query->getQuery(); 444 | $this->assertNotEmpty($q->wheres); 445 | $bindings = $q->getRawBindings()['where']; 446 | $this->assertNotEmpty($bindings); 447 | $this->assertContains('st_equals(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 448 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 449 | } 450 | 451 | public function testScopeIntersects() 452 | { 453 | $query = TestModel::intersects('point', $this->buildTestPolygon()); 454 | 455 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 456 | $q = $query->getQuery(); 457 | $this->assertNotEmpty($q->wheres); 458 | $bindings = $q->getRawBindings()['where']; 459 | $this->assertNotEmpty($bindings); 460 | $this->assertContains('st_intersects(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 461 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 462 | } 463 | 464 | public function testScopeOverlaps() 465 | { 466 | $query = TestModel::overlaps('point', $this->buildTestPolygon()); 467 | 468 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 469 | $q = $query->getQuery(); 470 | $this->assertNotEmpty($q->wheres); 471 | $bindings = $q->getRawBindings()['where']; 472 | $this->assertNotEmpty($bindings); 473 | $this->assertContains('st_overlaps(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 474 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 475 | } 476 | 477 | public function testScopeDoesTouch() 478 | { 479 | $query = TestModel::doesTouch('point', $this->buildTestPolygon()); 480 | 481 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 482 | $q = $query->getQuery(); 483 | $this->assertNotEmpty($q->wheres); 484 | $bindings = $q->getRawBindings()['where']; 485 | $this->assertNotEmpty($bindings); 486 | $this->assertContains('st_touches(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\'))', $q->wheres[0]['sql']); 487 | $this->assertEquals('POLYGON((1 1,2 1),(2 1,2 2),(2 2,1 1))', $bindings[0]); 488 | } 489 | 490 | public function testScopeOrderBySpatialThrowsExceptionWhenFunctionNotRegistered() 491 | { 492 | $point = new Point(1, 2); 493 | $this->assertException( 494 | \Grimzy\LaravelMysqlSpatial\Exceptions\UnknownSpatialFunctionException::class, 495 | 'does-not-exist' 496 | ); 497 | TestModel::orderBySpatial('point', $point, 'does-not-exist'); 498 | } 499 | 500 | public function testScopeOrderByDistance() 501 | { 502 | $point = new Point(1, 2); 503 | $query = TestModel::orderByDistance('point', $point); 504 | 505 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 506 | $q = $query->getQuery(); 507 | $this->assertNotEmpty($q->orders); 508 | $bindings = $q->getRawBindings()['order']; 509 | $this->assertNotEmpty($bindings); 510 | $this->assertContains('st_distance(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) asc', $q->orders[0]['sql']); 511 | $this->assertEquals('POINT(2 1)', $bindings[0]); 512 | } 513 | 514 | public function testScopeOrderByDistanceSphere() 515 | { 516 | $point = new Point(1, 2); 517 | $query = TestModel::orderByDistanceSphere('point', $point); 518 | 519 | $this->assertInstanceOf(\Grimzy\LaravelMysqlSpatial\Eloquent\Builder::class, $query); 520 | $q = $query->getQuery(); 521 | $this->assertNotEmpty($q->orders); 522 | $bindings = $q->getRawBindings()['order']; 523 | $this->assertNotEmpty($bindings); 524 | $this->assertContains('st_distance_sphere(`point`, ST_GeomFromText(?, ?, \'axis-order=long-lat\')) asc', $q->orders[0]['sql']); 525 | $this->assertEquals('POINT(2 1)', $bindings[0]); 526 | } 527 | } 528 | 529 | class TestModel extends Model 530 | { 531 | use \Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait; 532 | 533 | protected $spatialFields = ['point']; // TODO: only required when fetching, not saving 534 | 535 | public $timestamps = false; 536 | 537 | public static $pdo; 538 | 539 | public static function resolveConnection($connection = null) 540 | { 541 | if (is_null(static::$pdo)) { 542 | static::$pdo = m::mock('TestPDO')->makePartial(); 543 | } 544 | 545 | return new MysqlConnection(static::$pdo); 546 | } 547 | 548 | public function testrelatedmodels() 549 | { 550 | return $this->hasMany(TestRelatedModel::class); 551 | } 552 | 553 | public function testrelatedmodels2() 554 | { 555 | return $this->belongsToMany(TestRelatedModel::class); 556 | } 557 | } 558 | 559 | class TestRelatedModel extends TestModel 560 | { 561 | public function testmodel() 562 | { 563 | return $this->belongsTo(TestModel::class); 564 | } 565 | 566 | public function testmodels() 567 | { 568 | return $this->belongsToMany(TestModel::class); 569 | } 570 | } 571 | 572 | class TestNoSpatialModel extends Model 573 | { 574 | use \Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait; 575 | } 576 | 577 | class TestPDO extends PDO 578 | { 579 | public $queries = []; 580 | 581 | public $counter = 1; 582 | 583 | public function prepare($statement, $driver_options = []) 584 | { 585 | $this->queries[] = $statement; 586 | 587 | $stmt = m::mock('PDOStatement'); 588 | $stmt->shouldReceive('bindValue')->zeroOrMoreTimes(); 589 | $stmt->shouldReceive('execute'); 590 | $stmt->shouldReceive('fetchAll')->andReturn([['id' => 1, 'point' => 'POINT(1 2)']]); 591 | $stmt->shouldReceive('rowCount')->andReturn(1); 592 | 593 | return $stmt; 594 | } 595 | 596 | public function lastInsertId($name = null) 597 | { 598 | return $this->counter++; 599 | } 600 | 601 | public function resetQueries() 602 | { 603 | $this->queries = []; 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /tests/Unit/MysqlConnectionTest.php: -------------------------------------------------------------------------------- 1 | 'mysql', 'prefix' => 'prefix', 'database' => 'database', 'name' => 'foo']; 15 | $this->mysqlConnection = new MysqlConnection(new PDOStub(), 'database', 'prefix', $mysqlConfig); 16 | } 17 | 18 | public function testGetSchemaBuilder() 19 | { 20 | $builder = $this->mysqlConnection->getSchemaBuilder(); 21 | 22 | $this->assertInstanceOf(Builder::class, $builder); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Unit/Schema/BlueprintTest.php: -------------------------------------------------------------------------------- 1 | blueprint = Mockery::mock(Blueprint::class) 22 | ->makePartial()->shouldAllowMockingProtectedMethods(); 23 | } 24 | 25 | public function testGeometry() 26 | { 27 | $expectedCol = new ColumnDefinition([ 28 | 'type' => 'geometry', 29 | 'name' => 'col', 30 | 'srid' => null, 31 | ]); 32 | 33 | $this->blueprint 34 | ->shouldReceive('addColumn') 35 | ->with('geometry', 'col', ['srid' => null]) 36 | ->once() 37 | ->andReturn($expectedCol); 38 | 39 | $result = $this->blueprint->geometry('col'); 40 | 41 | $this->assertSame($expectedCol, $result); 42 | } 43 | 44 | public function testPoint() 45 | { 46 | $expectedCol = new ColumnDefinition([ 47 | 'type' => 'point', 48 | 'name' => 'col', 49 | 'srid' => null, 50 | ]); 51 | 52 | $this->blueprint 53 | ->shouldReceive('addColumn') 54 | ->with('point', 'col', ['srid' => null]) 55 | ->once() 56 | ->andReturn($expectedCol); 57 | 58 | $result = $this->blueprint->point('col'); 59 | 60 | $this->assertSame($expectedCol, $result); 61 | } 62 | 63 | public function testLinestring() 64 | { 65 | $expectedCol = new ColumnDefinition([ 66 | 'type' => 'linestring', 67 | 'name' => 'col', 68 | 'srid' => null, 69 | ]); 70 | 71 | $this->blueprint 72 | ->shouldReceive('addColumn') 73 | ->with('linestring', 'col', ['srid' => null]) 74 | ->once() 75 | ->andReturn($expectedCol); 76 | 77 | $result = $this->blueprint->linestring('col'); 78 | 79 | $this->assertSame($expectedCol, $result); 80 | } 81 | 82 | public function testPolygon() 83 | { 84 | $expectedCol = new ColumnDefinition([ 85 | 'type' => 'polygon', 86 | 'name' => 'col', 87 | 'srid' => null, 88 | ]); 89 | 90 | $this->blueprint 91 | ->shouldReceive('addColumn') 92 | ->with('polygon', 'col', ['srid' => null]) 93 | ->once() 94 | ->andReturn($expectedCol); 95 | 96 | $result = $this->blueprint->polygon('col'); 97 | 98 | $this->assertSame($expectedCol, $result); 99 | } 100 | 101 | public function testMultiPoint() 102 | { 103 | $expectedCol = new ColumnDefinition([ 104 | 'type' => 'multipoint', 105 | 'name' => 'col', 106 | 'srid' => null, 107 | ]); 108 | 109 | $this->blueprint 110 | ->shouldReceive('addColumn') 111 | ->with('multipoint', 'col', ['srid' => null]) 112 | ->once() 113 | ->andReturn($expectedCol); 114 | 115 | $result = $this->blueprint->multipoint('col'); 116 | 117 | $this->assertSame($expectedCol, $result); 118 | } 119 | 120 | public function testMultiLineString() 121 | { 122 | $expectedCol = new ColumnDefinition([ 123 | 'type' => 'multilinestring', 124 | 'name' => 'col', 125 | 'srid' => null, 126 | ]); 127 | 128 | $this->blueprint 129 | ->shouldReceive('addColumn') 130 | ->with('multilinestring', 'col', ['srid' => null]) 131 | ->once() 132 | ->andReturn($expectedCol); 133 | 134 | $result = $this->blueprint->multilinestring('col'); 135 | 136 | $this->assertSame($expectedCol, $result); 137 | } 138 | 139 | public function testMultiPolygon() 140 | { 141 | $expectedCol = new ColumnDefinition([ 142 | 'type' => 'multipolygon', 143 | 'name' => 'col', 144 | 'srid' => null, 145 | ]); 146 | 147 | $this->blueprint 148 | ->shouldReceive('addColumn') 149 | ->with('multipolygon', 'col', ['srid' => null]) 150 | ->once() 151 | ->andReturn($expectedCol); 152 | 153 | $result = $this->blueprint->multipolygon('col'); 154 | 155 | $this->assertSame($expectedCol, $result); 156 | } 157 | 158 | public function testGeometryCollection() 159 | { 160 | $expectedCol = new ColumnDefinition([ 161 | 'type' => 'geometrycollection', 162 | 'name' => 'col', 163 | 'srid' => null, 164 | ]); 165 | 166 | $this->blueprint 167 | ->shouldReceive('addColumn') 168 | ->with('geometrycollection', 'col', ['srid' => null]) 169 | ->once() 170 | ->andReturn($expectedCol); 171 | 172 | $result = $this->blueprint->geometrycollection('col'); 173 | 174 | $this->assertSame($expectedCol, $result); 175 | } 176 | 177 | public function testGeometryWithSrid() 178 | { 179 | $expectedCol = new ColumnDefinition([ 180 | 'type' => 'geometry', 181 | 'name' => 'col', 182 | 'srid' => 4326, 183 | ]); 184 | 185 | $this->blueprint 186 | ->shouldReceive('addColumn') 187 | ->with('geometry', 'col', ['srid' => 4326]) 188 | ->once() 189 | ->andReturn($expectedCol); 190 | 191 | $result = $this->blueprint->geometry('col', 4326); 192 | 193 | $this->assertSame($expectedCol, $result); 194 | } 195 | 196 | public function testPointWithSrid() 197 | { 198 | $expectedCol = new ColumnDefinition([ 199 | 'type' => 'point', 200 | 'name' => 'col', 201 | 'srid' => 4326, 202 | ]); 203 | 204 | $this->blueprint 205 | ->shouldReceive('addColumn') 206 | ->with('point', 'col', ['srid' => 4326]) 207 | ->once() 208 | ->andReturn($expectedCol); 209 | 210 | $result = $this->blueprint->point('col', 4326); 211 | 212 | $this->assertSame($expectedCol, $result); 213 | } 214 | 215 | public function testLinestringWithSrid() 216 | { 217 | $expectedCol = new ColumnDefinition([ 218 | 'type' => 'linestring', 219 | 'name' => 'col', 220 | 'srid' => 4326, 221 | ]); 222 | 223 | $this->blueprint 224 | ->shouldReceive('addColumn') 225 | ->with('linestring', 'col', ['srid' => 4326]) 226 | ->once() 227 | ->andReturn($expectedCol); 228 | 229 | $result = $this->blueprint->linestring('col', 4326); 230 | 231 | $this->assertSame($expectedCol, $result); 232 | } 233 | 234 | public function testPolygonWithSrid() 235 | { 236 | $expectedCol = new ColumnDefinition([ 237 | 'type' => 'polygon', 238 | 'name' => 'col', 239 | 'srid' => 4326, 240 | ]); 241 | 242 | $this->blueprint 243 | ->shouldReceive('addColumn') 244 | ->with('polygon', 'col', ['srid' => 4326]) 245 | ->once() 246 | ->andReturn($expectedCol); 247 | 248 | $result = $this->blueprint->polygon('col', 4326); 249 | 250 | $this->assertSame($expectedCol, $result); 251 | } 252 | 253 | public function testMultiPointWithSrid() 254 | { 255 | $expectedCol = new ColumnDefinition([ 256 | 'type' => 'multipoint', 257 | 'name' => 'col', 258 | 'srid' => 4326, 259 | ]); 260 | 261 | $this->blueprint 262 | ->shouldReceive('addColumn') 263 | ->with('multipoint', 'col', ['srid' => 4326]) 264 | ->once() 265 | ->andReturn($expectedCol); 266 | 267 | $result = $this->blueprint->multipoint('col', 4326); 268 | 269 | $this->assertSame($expectedCol, $result); 270 | } 271 | 272 | public function testMultiLineStringWithSrid() 273 | { 274 | $expectedCol = new ColumnDefinition([ 275 | 'type' => 'multilinestring', 276 | 'name' => 'col', 277 | 'srid' => 4326, 278 | ]); 279 | 280 | $this->blueprint 281 | ->shouldReceive('addColumn') 282 | ->with('multilinestring', 'col', ['srid' => 4326]) 283 | ->once() 284 | ->andReturn($expectedCol); 285 | 286 | $result = $this->blueprint->multilinestring('col', 4326); 287 | 288 | $this->assertSame($expectedCol, $result); 289 | } 290 | 291 | public function testMultiPolygonWithSrid() 292 | { 293 | $expectedCol = new ColumnDefinition([ 294 | 'type' => 'multipolygon', 295 | 'name' => 'col', 296 | 'srid' => 4326, 297 | ]); 298 | 299 | $this->blueprint 300 | ->shouldReceive('addColumn') 301 | ->with('multipolygon', 'col', ['srid' => 4326]) 302 | ->once() 303 | ->andReturn($expectedCol); 304 | 305 | $result = $this->blueprint->multipolygon('col', 4326); 306 | 307 | $this->assertSame($expectedCol, $result); 308 | } 309 | 310 | public function testGeometryCollectionWithSrid() 311 | { 312 | $expectedCol = new ColumnDefinition([ 313 | 'type' => 'geometrycollection', 314 | 'name' => 'col', 315 | 'srid' => 4326, 316 | ]); 317 | 318 | $this->blueprint 319 | ->shouldReceive('addColumn') 320 | ->with('geometrycollection', 'col', ['srid' => 4326]) 321 | ->once() 322 | ->andReturn($expectedCol); 323 | 324 | $result = $this->blueprint->geometrycollection('col', 4326); 325 | 326 | $this->assertSame($expectedCol, $result); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /tests/Unit/Schema/BuilderTest.php: -------------------------------------------------------------------------------- 1 | shouldReceive('getSchemaGrammar')->once()->andReturn(null); 17 | 18 | $mock = Mockery::mock(Builder::class, [$connection]); 19 | $mock->makePartial()->shouldAllowMockingProtectedMethods(); 20 | $blueprint = $mock->createBlueprint('test', function () { 21 | }); 22 | 23 | $this->assertInstanceOf(Blueprint::class, $blueprint); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/Unit/Schema/Grammars/MySqlGrammarTest.php: -------------------------------------------------------------------------------- 1 | geometry('foo'); 13 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 14 | 15 | $this->assertEquals(1, count($statements)); 16 | $this->assertEquals('alter table `test` add `foo` GEOMETRY not null', $statements[0]); 17 | } 18 | 19 | public function testAddingPoint() 20 | { 21 | $blueprint = new Blueprint('test'); 22 | $blueprint->point('foo'); 23 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 24 | 25 | $this->assertEquals(1, count($statements)); 26 | $this->assertEquals('alter table `test` add `foo` POINT not null', $statements[0]); 27 | } 28 | 29 | public function testAddingLinestring() 30 | { 31 | $blueprint = new Blueprint('test'); 32 | $blueprint->linestring('foo'); 33 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 34 | 35 | $this->assertEquals(1, count($statements)); 36 | $this->assertEquals('alter table `test` add `foo` LINESTRING not null', $statements[0]); 37 | } 38 | 39 | public function testAddingPolygon() 40 | { 41 | $blueprint = new Blueprint('test'); 42 | $blueprint->polygon('foo'); 43 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 44 | 45 | $this->assertEquals(1, count($statements)); 46 | $this->assertEquals('alter table `test` add `foo` POLYGON not null', $statements[0]); 47 | } 48 | 49 | public function testAddingMultipoint() 50 | { 51 | $blueprint = new Blueprint('test'); 52 | $blueprint->multipoint('foo'); 53 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 54 | 55 | $this->assertEquals(1, count($statements)); 56 | $this->assertEquals('alter table `test` add `foo` MULTIPOINT not null', $statements[0]); 57 | } 58 | 59 | public function testAddingMultiLinestring() 60 | { 61 | $blueprint = new Blueprint('test'); 62 | $blueprint->multilinestring('foo'); 63 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 64 | 65 | $this->assertEquals(1, count($statements)); 66 | $this->assertEquals('alter table `test` add `foo` MULTILINESTRING not null', $statements[0]); 67 | } 68 | 69 | public function testAddingMultiPolygon() 70 | { 71 | $blueprint = new Blueprint('test'); 72 | $blueprint->multipolygon('foo'); 73 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 74 | 75 | $this->assertEquals(1, count($statements)); 76 | $this->assertEquals('alter table `test` add `foo` MULTIPOLYGON not null', $statements[0]); 77 | } 78 | 79 | public function testAddingGeometryCollection() 80 | { 81 | $blueprint = new Blueprint('test'); 82 | $blueprint->geometrycollection('foo'); 83 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 84 | 85 | $this->assertEquals(1, count($statements)); 86 | $this->assertEquals('alter table `test` add `foo` GEOMETRYCOLLECTION not null', $statements[0]); 87 | } 88 | 89 | public function testAddingGeometryWithSrid() 90 | { 91 | $blueprint = new Blueprint('test'); 92 | $blueprint->geometry('foo', 4326); 93 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 94 | 95 | $this->assertEquals(1, count($statements)); 96 | $this->assertEquals('alter table `test` add `foo` GEOMETRY not null srid 4326', $statements[0]); 97 | } 98 | 99 | public function testAddingPointWithSrid() 100 | { 101 | $blueprint = new Blueprint('test'); 102 | $blueprint->point('foo', 4326); 103 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 104 | 105 | $this->assertEquals(1, count($statements)); 106 | $this->assertEquals('alter table `test` add `foo` POINT not null srid 4326', $statements[0]); 107 | } 108 | 109 | public function testAddingLinestringWithSrid() 110 | { 111 | $blueprint = new Blueprint('test'); 112 | $blueprint->linestring('foo', 4326); 113 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 114 | 115 | $this->assertEquals(1, count($statements)); 116 | $this->assertEquals('alter table `test` add `foo` LINESTRING not null srid 4326', $statements[0]); 117 | } 118 | 119 | public function testAddingPolygonWithSrid() 120 | { 121 | $blueprint = new Blueprint('test'); 122 | $blueprint->polygon('foo', 4326); 123 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 124 | 125 | $this->assertEquals(1, count($statements)); 126 | $this->assertEquals('alter table `test` add `foo` POLYGON not null srid 4326', $statements[0]); 127 | } 128 | 129 | public function testAddingMultipointWithSrid() 130 | { 131 | $blueprint = new Blueprint('test'); 132 | $blueprint->multipoint('foo', 4326); 133 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 134 | 135 | $this->assertEquals(1, count($statements)); 136 | $this->assertEquals('alter table `test` add `foo` MULTIPOINT not null srid 4326', $statements[0]); 137 | } 138 | 139 | public function testAddingMultiLinestringWithSrid() 140 | { 141 | $blueprint = new Blueprint('test'); 142 | $blueprint->multilinestring('foo', 4326); 143 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 144 | 145 | $this->assertEquals(1, count($statements)); 146 | $this->assertEquals('alter table `test` add `foo` MULTILINESTRING not null srid 4326', $statements[0]); 147 | } 148 | 149 | public function testAddingMultiPolygonWithSrid() 150 | { 151 | $blueprint = new Blueprint('test'); 152 | $blueprint->multipolygon('foo', 4326); 153 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 154 | 155 | $this->assertEquals(1, count($statements)); 156 | $this->assertEquals('alter table `test` add `foo` MULTIPOLYGON not null srid 4326', $statements[0]); 157 | } 158 | 159 | public function testAddingGeometryCollectionWithSrid() 160 | { 161 | $blueprint = new Blueprint('test'); 162 | $blueprint->geometrycollection('foo', 4326); 163 | $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 164 | 165 | $this->assertEquals(1, count($statements)); 166 | $this->assertEquals('alter table `test` add `foo` GEOMETRYCOLLECTION not null srid 4326', $statements[0]); 167 | } 168 | 169 | public function testAddRemoveSpatialIndex() 170 | { 171 | $blueprint = new Blueprint('test'); 172 | $blueprint->point('foo'); 173 | $blueprint->spatialIndex('foo'); 174 | $addStatements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 175 | 176 | $this->assertEquals(2, count($addStatements)); 177 | $this->assertEquals('alter table `test` add spatial `test_foo_spatial`(`foo`)', $addStatements[1]); 178 | 179 | $blueprint->dropSpatialIndex(['foo']); 180 | $blueprint->dropSpatialIndex('test_foo_spatial'); 181 | $dropStatements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); 182 | 183 | $expectedSql = 'alter table `test` drop index `test_foo_spatial`'; 184 | $this->assertEquals(5, count($dropStatements)); 185 | $this->assertEquals($expectedSql, $dropStatements[3]); 186 | $this->assertEquals($expectedSql, $dropStatements[4]); 187 | } 188 | 189 | /** 190 | * @return Connection 191 | */ 192 | protected function getConnection() 193 | { 194 | return Mockery::mock(MysqlConnection::class); 195 | } 196 | 197 | protected function getGrammar() 198 | { 199 | return new MySqlGrammar(); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /tests/Unit/Stubs/PDOStub.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(GeometryCollection::class, $geometryCollection); 18 | 19 | $this->assertEquals(2, $geometryCollection->count()); 20 | $this->assertInstanceOf(Point::class, $geometryCollection->getGeometries()[0]); 21 | $this->assertInstanceOf(LineString::class, $geometryCollection->getGeometries()[1]); 22 | } 23 | 24 | public function testToWKT() 25 | { 26 | $this->assertEquals('GEOMETRYCOLLECTION(LINESTRING(0 0,1 0,1 1,0 1,0 0),POINT(200 100))', $this->getGeometryCollection()->toWKT()); 27 | } 28 | 29 | public function testJsonSerialize() 30 | { 31 | $this->assertInstanceOf(\GeoJson\Geometry\GeometryCollection::class, $this->getGeometryCollection()->jsonSerialize()); 32 | 33 | $this->assertSame('{"type":"GeometryCollection","geometries":[{"type":"LineString","coordinates":[[0,0],[1,0],[1,1],[0,1],[0,0]]},{"type":"Point","coordinates":[200,100]}]}', json_encode($this->getGeometryCollection()->jsonSerialize())); 34 | } 35 | 36 | public function testCanCreateEmptyGeometryCollection() 37 | { 38 | $geometryCollection = new GeometryCollection([]); 39 | $this->assertInstanceOf(GeometryCollection::class, $geometryCollection); 40 | } 41 | 42 | public function testFromWKTWithEmptyGeometryCollection() 43 | { 44 | /** 45 | * @var GeometryCollection 46 | */ 47 | $geometryCollection = GeometryCollection::fromWKT('GEOMETRYCOLLECTION()'); 48 | $this->assertInstanceOf(GeometryCollection::class, $geometryCollection); 49 | 50 | $this->assertEquals(0, $geometryCollection->count()); 51 | } 52 | 53 | public function testToWKTWithEmptyGeometryCollection() 54 | { 55 | $this->assertEquals('GEOMETRYCOLLECTION()', (new GeometryCollection([]))->toWKT()); 56 | } 57 | 58 | public function testInvalidArgumentExceptionNotArrayGeometries() 59 | { 60 | $this->assertException( 61 | InvalidArgumentException::class, 62 | 'Grimzy\LaravelMysqlSpatial\Types\GeometryCollection must be a collection of Grimzy\LaravelMysqlSpatial\Types\GeometryInterface' 63 | ); 64 | $geometrycollection = new GeometryCollection([ 65 | $this->getPoint(), 66 | 1, 67 | ]); 68 | } 69 | 70 | public function testToArray() 71 | { 72 | $geometryCollection = $this->getGeometryCollection(); 73 | 74 | $this->assertInternalType('array', $geometryCollection->toArray()); 75 | } 76 | 77 | public function testIteratorAggregate() 78 | { 79 | $geometryCollection = $this->getGeometryCollection(); 80 | 81 | foreach ($geometryCollection as $value) { 82 | $this->assertInstanceOf(GeometryInterface::class, $value); 83 | } 84 | } 85 | 86 | public function testArrayAccess() 87 | { 88 | $linestring = $this->getLineString(); 89 | $point = $this->getPoint(); 90 | $geometryCollection = new GeometryCollection([$linestring, $point]); 91 | 92 | // assert getting 93 | $this->assertEquals($linestring, $geometryCollection[0]); 94 | $this->assertEquals($point, $geometryCollection[1]); 95 | 96 | // assert setting 97 | $polygon = $this->getPolygon(); 98 | $geometryCollection[] = $polygon; 99 | $this->assertEquals($polygon, $geometryCollection[2]); 100 | 101 | // assert unset 102 | unset($geometryCollection[0]); 103 | $this->assertNull($geometryCollection[0]); 104 | $this->assertEquals($point, $geometryCollection[1]); 105 | $this->assertEquals($polygon, $geometryCollection[2]); 106 | 107 | // assert insert 108 | $point100 = new Point(100, 100); 109 | $geometryCollection[100] = $point100; 110 | $this->assertEquals($point100, $geometryCollection[100]); 111 | 112 | // assert invalid 113 | $this->assertException( 114 | InvalidArgumentException::class, 115 | 'Grimzy\LaravelMysqlSpatial\Types\GeometryCollection must be a collection of Grimzy\LaravelMysqlSpatial\Types\GeometryInterface' 116 | ); 117 | $geometryCollection[] = 1; 118 | } 119 | 120 | public function testFromJson() 121 | { 122 | $geometryCollection = GeometryCollection::fromJson('{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[1,2]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[3,4]}}]}'); 123 | $this->assertInstanceOf(GeometryCollection::class, $geometryCollection); 124 | $geometryCollectionPoints = $geometryCollection->getGeometries(); 125 | $this->assertEquals(2, count($geometryCollectionPoints)); 126 | $this->assertEquals(new Point(2, 1), $geometryCollectionPoints[0]); 127 | $this->assertEquals(new Point(4, 3), $geometryCollectionPoints[1]); 128 | } 129 | 130 | public function testInvalidGeoJsonException() 131 | { 132 | $this->assertException( 133 | \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, 134 | sprintf('Expected %s, got %s', GeoJson\Feature\FeatureCollection::class, GeoJson\Geometry\Point::class) 135 | ); 136 | GeometryCollection::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 137 | } 138 | 139 | private function getGeometryCollection() 140 | { 141 | return new GeometryCollection([$this->getLineString(), $this->getPoint()]); 142 | } 143 | 144 | private function getLineString() 145 | { 146 | return new LineString([ 147 | new Point(0, 0), 148 | new Point(0, 1), 149 | new Point(1, 1), 150 | new Point(1, 0), 151 | new Point(0, 0), 152 | ]); 153 | } 154 | 155 | private function getPoint() 156 | { 157 | return new Point(100, 200); 158 | } 159 | 160 | private function getPolygon() 161 | { 162 | return new Polygon([ 163 | new LineString([ 164 | new Point(0, 0), 165 | new Point(0, 1), 166 | new Point(1, 1), 167 | new Point(1, 0), 168 | new Point(0, 0), 169 | ]), 170 | ]); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /tests/Unit/Types/GeometryTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('0 0', Geometry::getWKTArgument('POINT(0 0)')); 18 | $this->assertEquals('0 0,1 1,1 2', Geometry::getWKTArgument('LINESTRING(0 0,1 1,1 2)')); 19 | $this->assertEquals('(0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1)', Geometry::getWKTArgument('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))')); 20 | $this->assertEquals('(0 0),(1 2)', Geometry::getWKTArgument('MULTIPOINT((0 0),(1 2))')); 21 | $this->assertEquals('(0 0,1 1,1 2),(2 3,3 2,5 4)', Geometry::getWKTArgument('MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))')); 22 | $this->assertEquals('((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1))', Geometry::getWKTArgument('MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))')); 23 | $this->assertEquals('POINT(2 3),LINESTRING(2 3,3 4)', Geometry::getWKTArgument('GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))')); 24 | } 25 | 26 | public function testGetWKTClass() 27 | { 28 | $this->assertEquals(Point::class, Geometry::getWKTClass('POINT(0 0)')); 29 | $this->assertEquals(LineString::class, Geometry::getWKTClass('LINESTRING(0 0,1 1,1 2)')); 30 | $this->assertEquals(Polygon::class, Geometry::getWKTClass('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))')); 31 | $this->assertEquals(MultiPoint::class, Geometry::getWKTClass('MULTIPOINT((0 0),(1 2))')); 32 | $this->assertEquals(MultiLineString::class, Geometry::getWKTClass('MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))')); 33 | $this->assertEquals(MultiPolygon::class, Geometry::getWKTClass('MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))')); 34 | $this->assertEquals(GeometryCollection::class, Geometry::getWKTClass('GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))')); 35 | $this->assertException( 36 | UnknownWKTTypeException::class, 37 | 'Type was TRIANGLE' 38 | ); 39 | Geometry::getWKTClass('TRIANGLE((0 0, 0 9, 9 0, 0 0))'); 40 | } 41 | 42 | public function testGetWKBClass() 43 | { 44 | $prefix = "\0\0\0\0"; 45 | 46 | $this->assertInstanceOf(Point::class, Geometry::fromWKB($prefix.'0101000000000000000000f03f0000000000000040')); 47 | 48 | $this->assertInstanceOf(LineString::class, Geometry::fromWKB($prefix.'010200000002000000000000000000f03f000000000000004000000000000008400000000000001040')); 49 | $this->assertInstanceOf(Polygon::class, Geometry::fromWKB($prefix.'01030000000100000004000000000000000000f03f00000000000000400000000000000840000000000000104000000000000014400000000000001840000000000000f03f0000000000000040')); 50 | $this->assertInstanceOf(MultiPoint::class, Geometry::fromWKB($prefix.'0104000000020000000101000000000000000000f03f0000000000000040010100000000000000000008400000000000001040')); 51 | $this->assertInstanceOf(MultiLineString::class, Geometry::fromWKB($prefix.'010500000001000000010200000002000000000000000000f03f000000000000004000000000000008400000000000001040')); 52 | $this->assertInstanceOf(MultiLineString::class, Geometry::fromWKB($prefix.'010500000002000000010200000002000000000000000000f03f000000000000004000000000000008400000000000001040010200000002000000000000000000144000000000000018400000000000001c400000000000002040')); 53 | $this->assertInstanceOf(MultiPolygon::class, Geometry::fromWKB($prefix.'01060000000200000001030000000100000004000000000000000000f03f00000000000000400000000000000840000000000000104000000000000014400000000000001840000000000000f03f000000000000004001030000000300000004000000000000000000f03f00000000000000400000000000000840000000000000104000000000000014400000000000001840000000000000f03f000000000000004004000000000000000000264000000000000028400000000000002a400000000000002c400000000000002e4000000000000030400000000000002640000000000000284004000000000000000000354000000000000036400000000000003740000000000000384000000000000039400000000000003a4000000000000035400000000000003640')); 54 | $this->assertInstanceOf(GeometryCollection::class, Geometry::fromWKB($prefix.'0107000000010000000101000000000000000000f03f0000000000000040')); 55 | $this->assertInstanceOf(GeometryCollection::class, Geometry::fromWKB($prefix.'0107000000020000000101000000000000000000f03f0000000000000040010200000002000000000000000000f03f000000000000004000000000000008400000000000001040')); 56 | 57 | $this->assertInstanceOf(Point::class, Geometry::fromWKB($prefix.'0101000000000000000000f03f0000000000000040')); 58 | } 59 | 60 | public function testFromJsonPoint() 61 | { 62 | $point = Geometry::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 63 | $this->assertInstanceOf(Point::class, $point); 64 | $this->assertEquals(1.2, $point->getLat()); 65 | $this->assertEquals(3.4, $point->getLng()); 66 | } 67 | 68 | public function testFromJsonLineString() 69 | { 70 | $lineString = Geometry::fromJson('{"type": "LineString","coordinates":[[1,1],[2,2]]}'); 71 | $this->assertInstanceOf(LineString::class, $lineString); 72 | $lineStringPoints = $lineString->getGeometries(); 73 | $this->assertEquals(new Point(1, 1), $lineStringPoints[0]); 74 | $this->assertEquals(new Point(2, 2), $lineStringPoints[1]); 75 | } 76 | 77 | public function testFromJsonPolygon() 78 | { 79 | $polygon1 = Geometry::fromJson('{"type": "Polygon","coordinates":[[[1,1],[2,1],[2,2],[1,2],[1,1]]]}'); 80 | $this->assertInstanceOf(Polygon::class, $polygon1); 81 | $polygonLineStrings1 = $polygon1->getGeometries(); 82 | $this->assertEquals(1, count($polygonLineStrings1)); 83 | $this->assertEquals(new Point(1, 1), $polygonLineStrings1[0][0]); 84 | $this->assertEquals(new Point(1, 2), $polygonLineStrings1[0][1]); 85 | $this->assertEquals(new Point(2, 2), $polygonLineStrings1[0][2]); 86 | $this->assertEquals(new Point(2, 1), $polygonLineStrings1[0][3]); 87 | $this->assertEquals(new Point(1, 1), $polygonLineStrings1[0][4]); 88 | 89 | $polygon2 = Geometry::fromJson('{"type":"Polygon","coordinates":[[[1,1],[2,1],[2,2],[1,2],[1,1]],[[1.2,1.2],[1.6,1.2],[1.6,1.8],[1.2,1.8],[1.2,1.2]]]}'); 90 | $this->assertInstanceOf(Polygon::class, $polygon2); 91 | $polygonLineStrings2 = $polygon2->getGeometries(); 92 | $this->assertEquals(2, count($polygonLineStrings2)); 93 | $this->assertEquals(new Point(1, 1), $polygonLineStrings2[0][0]); 94 | $this->assertEquals(new Point(1, 2), $polygonLineStrings2[0][1]); 95 | $this->assertEquals(new Point(2, 2), $polygonLineStrings2[0][2]); 96 | $this->assertEquals(new Point(2, 1), $polygonLineStrings2[0][3]); 97 | $this->assertEquals(new Point(1, 1), $polygonLineStrings2[0][4]); 98 | $this->assertEquals(new Point(1.2, 1.2), $polygonLineStrings2[1][0]); 99 | $this->assertEquals(new Point(1.2, 1.6), $polygonLineStrings2[1][1]); 100 | $this->assertEquals(new Point(1.8, 1.6), $polygonLineStrings2[1][2]); 101 | $this->assertEquals(new Point(1.8, 1.2), $polygonLineStrings2[1][3]); 102 | $this->assertEquals(new Point(1.2, 1.2), $polygonLineStrings2[1][4]); 103 | } 104 | 105 | public function testFromJsonMultiPoint() 106 | { 107 | $multiPoint = Geometry::fromJson('{"type":"MultiPoint","coordinates":[[1,1],[2,1],[2,2]]}'); 108 | $this->assertInstanceOf(MultiPoint::class, $multiPoint); 109 | $multiPointPoints = $multiPoint->getGeometries(); 110 | $this->assertEquals(3, count($multiPointPoints)); 111 | $this->assertEquals(new Point(1, 1), $multiPointPoints[0]); 112 | $this->assertEquals(new Point(1, 2), $multiPointPoints[1]); 113 | $this->assertEquals(new Point(2, 2), $multiPointPoints[2]); 114 | } 115 | 116 | public function testFromJsonMultiLineString() 117 | { 118 | $multiLineString = Geometry::fromJson('{"type":"MultiLineString","coordinates":[[[1,1],[1,2],[1,3]],[[2,1],[2,2],[2,3]]]}'); 119 | $this->assertInstanceOf(MultiLineString::class, $multiLineString); 120 | $multiLineStringLineStrings = $multiLineString->getGeometries(); 121 | $this->assertEquals(2, count($multiLineStringLineStrings)); 122 | $this->assertEquals(new Point(1, 1), $multiLineStringLineStrings[0][0]); 123 | $this->assertEquals(new Point(2, 1), $multiLineStringLineStrings[0][1]); 124 | $this->assertEquals(new Point(3, 1), $multiLineStringLineStrings[0][2]); 125 | $this->assertEquals(new Point(1, 2), $multiLineStringLineStrings[1][0]); 126 | $this->assertEquals(new Point(2, 2), $multiLineStringLineStrings[1][1]); 127 | $this->assertEquals(new Point(3, 2), $multiLineStringLineStrings[1][2]); 128 | } 129 | 130 | public function testFromJsonMultiPolygon() 131 | { 132 | $multiPolygon = Geometry::fromJson('{"type":"MultiPolygon","coordinates":[[[[1,1],[1,2],[2,2],[2,1],[1,1]]],[[[0,0],[0,1],[1,1],[1,0],[0,0]]]]}'); 133 | $this->assertInstanceOf(MultiPolygon::class, $multiPolygon); 134 | $multiPolygonPolygons = $multiPolygon->getGeometries(); 135 | $this->assertEquals(2, count($multiPolygonPolygons)); 136 | $this->assertEquals(new Polygon([new LineString([ 137 | new Point(1, 1), 138 | new Point(2, 1), 139 | new Point(2, 2), 140 | new Point(1, 2), 141 | new Point(1, 1), 142 | ])]), $multiPolygonPolygons[0]); 143 | $this->assertEquals(new Polygon([new LineString([ 144 | new Point(0, 0), 145 | new Point(1, 0), 146 | new Point(1, 1), 147 | new Point(0, 1), 148 | new Point(0, 0), 149 | ])]), $multiPolygonPolygons[1]); 150 | } 151 | 152 | public function testFromJsonPointFeature() 153 | { 154 | $point = Geometry::fromJson('{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[3.4,1.2]}}'); 155 | $this->assertInstanceOf(Point::class, $point); 156 | $this->assertEquals(1.2, $point->getLat()); 157 | $this->assertEquals(3.4, $point->getLng()); 158 | } 159 | 160 | public function testFromJsonMultiPointFeatureCollection() 161 | { 162 | $geometryCollection = Geometry::fromJson('{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[1,2]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[3,4]}}]}'); 163 | $this->assertInstanceOf(GeometryCollection::class, $geometryCollection); 164 | $geometryCollectionPoints = $geometryCollection->getGeometries(); 165 | $this->assertEquals(2, count($geometryCollectionPoints)); 166 | $this->assertEquals(new Point(2, 1), $geometryCollectionPoints[0]); 167 | $this->assertEquals(new Point(4, 3), $geometryCollectionPoints[1]); 168 | } 169 | 170 | public function testToJson() 171 | { 172 | $point = new Point(1, 1); 173 | 174 | $this->assertSame('{"type":"Point","coordinates":[1,1]}', $point->toJson()); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /tests/Unit/Types/LineStringTest.php: -------------------------------------------------------------------------------- 1 | points = [new Point(0, 0), new Point(1, 1), new Point(2, 2)]; 13 | } 14 | 15 | public function testToWKT() 16 | { 17 | $linestring = new LineString($this->points); 18 | 19 | $this->assertEquals('LINESTRING(0 0,1 1,2 2)', $linestring->toWKT()); 20 | } 21 | 22 | public function testFromWKT() 23 | { 24 | $linestring = LineString::fromWKT('LINESTRING(0 0, 1 1, 2 2)'); 25 | $this->assertInstanceOf(LineString::class, $linestring); 26 | 27 | $this->assertEquals(3, $linestring->count()); 28 | } 29 | 30 | public function testToString() 31 | { 32 | $linestring = new LineString($this->points); 33 | 34 | $this->assertEquals('0 0,1 1,2 2', (string) $linestring); 35 | } 36 | 37 | public function testFromJson() 38 | { 39 | $lineString = LineString::fromJson('{"type": "LineString","coordinates":[[1,1],[2,2]]}'); 40 | $this->assertInstanceOf(LineString::class, $lineString); 41 | $lineStringPoints = $lineString->getGeometries(); 42 | $this->assertEquals(new Point(1, 1), $lineStringPoints[0]); 43 | $this->assertEquals(new Point(2, 2), $lineStringPoints[1]); 44 | } 45 | 46 | public function testInvalidGeoJsonException() 47 | { 48 | $this->assertException( 49 | \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, 50 | sprintf('Expected %s, got %s', \GeoJson\Geometry\LineString::class, GeoJson\Geometry\Point::class) 51 | ); 52 | LineString::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 53 | } 54 | 55 | public function testJsonSerialize() 56 | { 57 | $lineString = new LineString($this->points); 58 | 59 | $this->assertInstanceOf(\GeoJson\Geometry\LineString::class, $lineString->jsonSerialize()); 60 | $this->assertSame('{"type":"LineString","coordinates":[[0,0],[1,1],[2,2]]}', json_encode($lineString)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/Unit/Types/MultiLineStringTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(MultiLineString::class, $multilinestring); 13 | 14 | $this->assertSame(2, $multilinestring->count()); 15 | } 16 | 17 | public function testToWKT() 18 | { 19 | $collection = new LineString([ 20 | new Point(0, 0), 21 | new Point(0, 1), 22 | new Point(1, 1), 23 | new Point(1, 0), 24 | new Point(0, 0), 25 | ]); 26 | 27 | $multilinestring = new MultiLineString([$collection]); 28 | 29 | $this->assertSame('MULTILINESTRING((0 0,1 0,1 1,0 1,0 0))', $multilinestring->toWKT()); 30 | } 31 | 32 | public function testFromJson() 33 | { 34 | $multiLineString = MultiLineString::fromJson('{"type":"MultiLineString","coordinates":[[[1,1],[1,2],[1,3]],[[2,1],[2,2],[2,3]]]}'); 35 | $this->assertInstanceOf(MultiLineString::class, $multiLineString); 36 | $multiLineStringLineStrings = $multiLineString->getGeometries(); 37 | $this->assertEquals(2, count($multiLineStringLineStrings)); 38 | $this->assertEquals(new Point(1, 1), $multiLineStringLineStrings[0][0]); 39 | $this->assertEquals(new Point(2, 1), $multiLineStringLineStrings[0][1]); 40 | $this->assertEquals(new Point(3, 1), $multiLineStringLineStrings[0][2]); 41 | $this->assertEquals(new Point(1, 2), $multiLineStringLineStrings[1][0]); 42 | $this->assertEquals(new Point(2, 2), $multiLineStringLineStrings[1][1]); 43 | $this->assertEquals(new Point(3, 2), $multiLineStringLineStrings[1][2]); 44 | } 45 | 46 | public function testInvalidGeoJsonException() 47 | { 48 | $this->assertException( 49 | \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, 50 | sprintf('Expected %s, got %s', GeoJson\Geometry\MultiLineString::class, GeoJson\Geometry\Point::class) 51 | ); 52 | MultiLineString::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 53 | } 54 | 55 | public function testJsonSerialize() 56 | { 57 | $multilinestring = MultiLineString::fromWKT('MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))'); 58 | 59 | $this->assertInstanceOf(\GeoJson\Geometry\MultiLineString::class, $multilinestring->jsonSerialize()); 60 | $this->assertSame('{"type":"MultiLineString","coordinates":[[[0,0],[1,1],[1,2]],[[2,3],[3,2],[5,4]]]}', json_encode($multilinestring)); 61 | } 62 | 63 | public function testInvalidArgumentExceptionAtLeastOneEntry() 64 | { 65 | $this->assertException( 66 | InvalidArgumentException::class, 67 | 'Grimzy\LaravelMysqlSpatial\Types\MultiLineString must contain at least 1 entry' 68 | ); 69 | $multilinestring = new MultiLineString([]); 70 | } 71 | 72 | public function testInvalidArgumentExceptionNotArrayOfLineString() 73 | { 74 | $this->assertException( 75 | InvalidArgumentException::class, 76 | 'Grimzy\LaravelMysqlSpatial\Types\MultiLineString must be a collection of Grimzy\LaravelMysqlSpatial\Types\LineString' 77 | ); 78 | $multilinestring = new MultiLineString([ 79 | new LineString([new Point(0, 0), new Point(1, 1)]), 80 | new Point(0, 1), 81 | ]); 82 | } 83 | 84 | public function testArrayAccess() 85 | { 86 | $linestring0 = new LineString([ 87 | new Point(0, 0), 88 | new Point(1, 1), 89 | ]); 90 | $linestring1 = new LineString([ 91 | new Point(1, 1), 92 | new Point(2, 2), 93 | ]); 94 | 95 | $multilinestring = new MultiLineString([$linestring0, $linestring1]); 96 | 97 | // assert getting 98 | $this->assertEquals($linestring0, $multilinestring[0]); 99 | $this->assertEquals($linestring1, $multilinestring[1]); 100 | 101 | // assert setting 102 | $linestring2 = new LineString([ 103 | new Point(2, 2), 104 | new Point(3, 3), 105 | ]); 106 | $multilinestring[] = $linestring2; 107 | $this->assertEquals($linestring2, $multilinestring[2]); 108 | 109 | // assert invalid 110 | $this->assertException( 111 | InvalidArgumentException::class, 112 | 'Grimzy\LaravelMysqlSpatial\Types\MultiLineString must be a collection of Grimzy\LaravelMysqlSpatial\Types\LineString' 113 | ); 114 | $multilinestring[] = 1; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /tests/Unit/Types/MultiPointTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(MultiPoint::class, $multipoint); 12 | 13 | $this->assertEquals(3, $multipoint->count()); 14 | } 15 | 16 | public function testToWKT() 17 | { 18 | $collection = [new Point(0, 0), new Point(0, 1), new Point(1, 1)]; 19 | 20 | $multipoint = new MultiPoint($collection); 21 | 22 | $this->assertEquals('MULTIPOINT((0 0),(1 0),(1 1))', $multipoint->toWKT()); 23 | } 24 | 25 | public function testGetPoints() 26 | { 27 | $multipoint = MultiPoint::fromWKT('MULTIPOINT((0 0),(1 0),(1 1))'); 28 | 29 | $this->assertInstanceOf(Point::class, $multipoint->getPoints()[0]); 30 | } 31 | 32 | public function testFromJson() 33 | { 34 | $multiPoint = MultiPoint::fromJson('{"type":"MultiPoint","coordinates":[[1,1],[2,1],[2,2]]}'); 35 | $this->assertInstanceOf(MultiPoint::class, $multiPoint); 36 | $multiPointPoints = $multiPoint->getGeometries(); 37 | $this->assertEquals(3, count($multiPointPoints)); 38 | $this->assertEquals(new Point(1, 1), $multiPointPoints[0]); 39 | $this->assertEquals(new Point(1, 2), $multiPointPoints[1]); 40 | $this->assertEquals(new Point(2, 2), $multiPointPoints[2]); 41 | } 42 | 43 | public function testInvalidGeoJsonException() 44 | { 45 | $this->assertException( 46 | \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, 47 | sprintf('Expected %s, got %s', GeoJson\Geometry\MultiPoint::class, GeoJson\Geometry\Point::class) 48 | ); 49 | MultiPoint::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 50 | } 51 | 52 | public function testJsonSerialize() 53 | { 54 | $collection = [new Point(0, 0), new Point(0, 1), new Point(1, 1)]; 55 | 56 | $multipoint = new MultiPoint($collection); 57 | 58 | $this->assertInstanceOf(\GeoJson\Geometry\MultiPoint::class, $multipoint->jsonSerialize()); 59 | $this->assertSame('{"type":"MultiPoint","coordinates":[[0,0],[1,0],[1,1]]}', json_encode($multipoint)); 60 | } 61 | 62 | public function testInvalidArgumentExceptionAtLeastOneEntry() 63 | { 64 | $this->assertException( 65 | InvalidArgumentException::class, 66 | 'Grimzy\LaravelMysqlSpatial\Types\MultiPoint must contain at least 1 entry' 67 | ); 68 | $multipoint = new MultiPoint([]); 69 | } 70 | 71 | public function testInvalidArgumentExceptionNotArrayOfLineString() 72 | { 73 | $this->assertException( 74 | InvalidArgumentException::class, 75 | 'Grimzy\LaravelMysqlSpatial\Types\MultiPoint must be a collection of Grimzy\LaravelMysqlSpatial\Types\Point' 76 | ); 77 | $multipoint = new MultiPoint([ 78 | new Point(0, 0), 79 | 1, 80 | ]); 81 | } 82 | 83 | public function testArrayAccess() 84 | { 85 | $point0 = new Point(0, 0); 86 | $point1 = new Point(1, 1); 87 | $multipoint = new MultiPoint([$point0, $point1]); 88 | 89 | // assert getting 90 | $this->assertEquals($point0, $multipoint[0]); 91 | $this->assertEquals($point1, $multipoint[1]); 92 | 93 | // assert setting 94 | $point2 = new Point(2, 2); 95 | $multipoint[] = $point2; 96 | $this->assertEquals($point2, $multipoint[2]); 97 | 98 | // assert invalid 99 | $this->assertException( 100 | InvalidArgumentException::class, 101 | 'Grimzy\LaravelMysqlSpatial\Types\MultiPoint must be a collection of Grimzy\LaravelMysqlSpatial\Types\Point' 102 | ); 103 | $multipoint[] = 1; 104 | } 105 | 106 | public function testDeprecatedPrependPoint() 107 | { 108 | $point1 = new Point(1, 1); 109 | $point2 = new Point(2, 2); 110 | $multipoint = new MultiPoint([$point1, $point2]); 111 | 112 | $point0 = new Point(0, 0); 113 | $multipoint->prependPoint($point0); 114 | 115 | $this->assertEquals($point0, $multipoint[0]); 116 | $this->assertEquals($point1, $multipoint[1]); 117 | $this->assertEquals($point2, $multipoint[2]); 118 | } 119 | 120 | public function testDeprecatedAppendPoint() 121 | { 122 | $point0 = new Point(0, 0); 123 | $point1 = new Point(1, 1); 124 | $multipoint = new MultiPoint([$point0, $point1]); 125 | 126 | $point2 = new Point(2, 2); 127 | $multipoint->appendPoint($point2); 128 | 129 | $this->assertEquals($point0, $multipoint[0]); 130 | $this->assertEquals($point1, $multipoint[1]); 131 | $this->assertEquals($point2, $multipoint[2]); 132 | } 133 | 134 | public function testDeprecatedInsertPoint() 135 | { 136 | $point1 = new Point(1, 1); 137 | $point3 = new Point(3, 3); 138 | $multipoint = new MultiPoint([$point1, $point3]); 139 | 140 | $point2 = new Point(2, 2); 141 | $multipoint->insertPoint(1, $point2); 142 | 143 | $this->assertEquals($point1, $multipoint[0]); 144 | $this->assertEquals($point2, $multipoint[1]); 145 | $this->assertEquals($point3, $multipoint[2]); 146 | 147 | $this->assertException( 148 | InvalidArgumentException::class, 149 | '$index is greater than the size of the array' 150 | ); 151 | $multipoint->insertPoint(100, new Point(100, 100)); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /tests/Unit/Types/MultiPolygonTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(MultiPolygon::class, $polygon); 14 | 15 | $this->assertEquals(2, $polygon->count()); 16 | 17 | $polygon = MultiPolygon::fromWKT('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))'); 18 | $this->assertInstanceOf(MultiPolygon::class, $polygon); 19 | 20 | $this->assertEquals(2, $polygon->count()); 21 | 22 | $this->assertEquals('MULTIPOLYGON(((30 20,45 40,10 40,30 20)),((15 5,40 10,10 20,5 10,15 5)))', $polygon->toWKT()); 23 | } 24 | 25 | public function testToWKT() 26 | { 27 | $this->assertEquals('MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0),(10 10,20 10,20 20,10 20,10 10)),((100 100,200 100,200 200,100 200,100 100)))', $this->getMultiPolygon()->toWKT()); 28 | } 29 | 30 | public function testGetPolygons() 31 | { 32 | $polygon = MultiPolygon::fromWKT('MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))'); 33 | 34 | $this->assertInstanceOf(Polygon::class, $polygon->getPolygons()[0]); 35 | } 36 | 37 | public function testIssue12() 38 | { 39 | $polygon = MultiPolygon::fromWKT('MULTIPOLYGON(((-80.214554 25.769598 0 0,-80.2147 25.774514 0 0,-80.212983 25.77456 0 0,-80.212977 25.773597 0 0,-80.211448 25.773655 0 0,-80.211498 25.774579 0 0,-80.209432 25.774665 0 0,-80.209392 25.773667 0 0,-80.204387 25.773834 0 0,-80.199383 25.774324 0 0,-80.197718 25.774031 0 0,-80.197757 25.774975 0 0,-80.193655 25.775108 0 0,-80.193623 25.774134 0 0,-80.191855 25.772551 0 0,-80.193442 25.76969 0 0,-80.192231 25.768345 0 0,-80.192879 25.758009 0 0,-80.196301 25.759985 0 0,-80.195608 25.76152 0 0,-80.198856 25.761454 0 0,-80.200646 25.763287 0 0,-80.20401 25.763164 0 0,-80.204023 25.76367 0 0,-80.205673 25.763141 0 0,-80.214326 25.762935 0 0,-80.214451 25.765883 0 0,-80.214539 25.768649 0 0,-80.216203 25.76858 0 0,-80.214554 25.769598 0 0)))'); 40 | 41 | $this->assertInstanceOf(MultiPolygon::class, $polygon); 42 | } 43 | 44 | public function testFromJson() 45 | { 46 | $multiPolygon = MultiPolygon::fromJson('{"type":"MultiPolygon","coordinates":[[[[1,1],[1,2],[2,2],[2,1],[1,1]]],[[[0,0],[0,1],[1,1],[1,0],[0,0]]]]}'); 47 | $this->assertInstanceOf(MultiPolygon::class, $multiPolygon); 48 | $multiPolygonPolygons = $multiPolygon->getGeometries(); 49 | $this->assertEquals(2, count($multiPolygonPolygons)); 50 | $this->assertEquals(new Polygon([new LineString([ 51 | new Point(1, 1), 52 | new Point(2, 1), 53 | new Point(2, 2), 54 | new Point(1, 2), 55 | new Point(1, 1), 56 | ])]), $multiPolygonPolygons[0]); 57 | $this->assertEquals(new Polygon([new LineString([ 58 | new Point(0, 0), 59 | new Point(1, 0), 60 | new Point(1, 1), 61 | new Point(0, 1), 62 | new Point(0, 0), 63 | ])]), $multiPolygonPolygons[1]); 64 | } 65 | 66 | public function testInvalidGeoJsonException() 67 | { 68 | $this->assertException( 69 | \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, 70 | sprintf('Expected %s, got %s', GeoJson\Geometry\MultiPolygon::class, GeoJson\Geometry\Point::class) 71 | ); 72 | MultiPolygon::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 73 | } 74 | 75 | public function testJsonSerialize() 76 | { 77 | $this->assertInstanceOf(\GeoJson\Geometry\MultiPolygon::class, $this->getMultiPolygon()->jsonSerialize()); 78 | $this->assertSame('{"type":"MultiPolygon","coordinates":[[[[0,0],[1,0],[1,1],[0,1],[0,0]],[[10,10],[20,10],[20,20],[10,20],[10,10]]],[[[100,100],[200,100],[200,200],[100,200],[100,100]]]]}', json_encode($this->getMultiPolygon())); 79 | } 80 | 81 | public function testInvalidArgumentExceptionAtLeastOneEntry() 82 | { 83 | $this->assertException( 84 | InvalidArgumentException::class, 85 | 'Grimzy\LaravelMysqlSpatial\Types\MultiPolygon must contain at least 1 entry' 86 | ); 87 | $multipolygon = new MultiPolygon([]); 88 | } 89 | 90 | public function testInvalidArgumentExceptionNotArrayOfPolygon() 91 | { 92 | $this->assertException( 93 | InvalidArgumentException::class, 94 | 'Grimzy\LaravelMysqlSpatial\Types\MultiPolygon must be a collection of Grimzy\LaravelMysqlSpatial\Types\Polygon' 95 | ); 96 | $multipolygon = new MultiPolygon([ 97 | $this->getPolygon1(), 98 | $this->getLineString1(), 99 | ]); 100 | } 101 | 102 | public function testArrayAccess() 103 | { 104 | $polygon0 = $this->getPolygon1(); 105 | $polygon1 = $this->getPolygon2(); 106 | 107 | $multipolygon = new MultiPolygon([$polygon0, $polygon1]); 108 | 109 | // assert getting 110 | $this->assertEquals($polygon0, $multipolygon[0]); 111 | $this->assertEquals($polygon1, $multipolygon[1]); 112 | 113 | // assert setting 114 | $polygon2 = $this->getPolygon3(); 115 | $multipolygon[] = $polygon2; 116 | $this->assertEquals($polygon2, $multipolygon[2]); 117 | 118 | // assert invalid 119 | $this->assertException( 120 | InvalidArgumentException::class, 121 | 'Grimzy\LaravelMysqlSpatial\Types\MultiPolygon must be a collection of Grimzy\LaravelMysqlSpatial\Types\Polygon' 122 | ); 123 | $multipolygon[] = 1; 124 | } 125 | 126 | private function getMultiPolygon() 127 | { 128 | return new MultiPolygon([$this->getPolygon1(), $this->getPolygon2()]); 129 | } 130 | 131 | private function getLineString1() 132 | { 133 | return new LineString([ 134 | new Point(0, 0), 135 | new Point(0, 1), 136 | new Point(1, 1), 137 | new Point(1, 0), 138 | new Point(0, 0), 139 | ]); 140 | } 141 | 142 | private function getLineString2() 143 | { 144 | return new LineString([ 145 | new Point(10, 10), 146 | new Point(10, 20), 147 | new Point(20, 20), 148 | new Point(20, 10), 149 | new Point(10, 10), 150 | ]); 151 | } 152 | 153 | private function getLineString3() 154 | { 155 | return new LineString([ 156 | new Point(100, 100), 157 | new Point(100, 200), 158 | new Point(200, 200), 159 | new Point(200, 100), 160 | new Point(100, 100), 161 | ]); 162 | } 163 | 164 | private function getPolygon1() 165 | { 166 | return new Polygon([$this->getLineString1(), $this->getLineString2()]); 167 | } 168 | 169 | private function getPolygon2() 170 | { 171 | return new Polygon([$this->getLineString3()]); 172 | } 173 | 174 | private function getPolygon3() 175 | { 176 | return new Polygon([ 177 | new LineString([ 178 | new Point(10, 10), 179 | new Point(10, 20), 180 | new Point(20, 20), 181 | new Point(20, 10), 182 | new Point(10, 10), 183 | ]), 184 | ]); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /tests/Unit/Types/PointTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(Point::class, $point); 12 | $this->assertSame(2.0, $point->getLat()); 13 | $this->assertSame(1.0, $point->getLng()); 14 | $this->assertSame(4326, $point->getSrid()); 15 | } 16 | 17 | public function testToWKT() 18 | { 19 | $point = new Point(1, 2, 4326); 20 | 21 | $this->assertSame('POINT(2 1)', $point->toWKT()); 22 | } 23 | 24 | public function testGettersAndSetters() 25 | { 26 | $point = new Point(1, 2); 27 | $this->assertSame(1.0, $point->getLat()); 28 | $this->assertSame(2.0, $point->getLng()); 29 | $this->assertSame(0, $point->getSrid()); 30 | 31 | $point->setLat('3'); 32 | $point->setLng('4'); 33 | $point->setSrid(100); 34 | 35 | $this->assertSame(3.0, $point->getLat()); 36 | $this->assertSame(4.0, $point->getLng()); 37 | $this->assertSame(100, $point->getSrid()); 38 | } 39 | 40 | public function testPair() 41 | { 42 | $point = Point::fromPair('1.5 2', 4326); 43 | 44 | $this->assertSame(1.5, $point->getLng()); 45 | $this->assertSame(2.0, $point->getLat()); 46 | $this->assertSame(4326, $point->getSrid()); 47 | 48 | $this->assertSame('1.5 2', $point->toPair()); 49 | } 50 | 51 | public function testToString() 52 | { 53 | $point = Point::fromString('1.3 2', 4326); 54 | 55 | $this->assertSame(1.3, $point->getLng()); 56 | $this->assertSame(2.0, $point->getLat()); 57 | $this->assertSame(4326, $point->getSrid()); 58 | 59 | $this->assertEquals('1.3 2', (string) $point); 60 | } 61 | 62 | public function testFromJson() 63 | { 64 | $point = Point::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 65 | $this->assertInstanceOf(Point::class, $point); 66 | $this->assertSame(1.2, $point->getLat()); 67 | $this->assertSame(3.4, $point->getLng()); 68 | } 69 | 70 | public function testInvalidGeoJsonException() 71 | { 72 | $this->assertException( 73 | \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, 74 | 'Expected GeoJson\Geometry\Point, got GeoJson\Geometry\LineString' 75 | ); 76 | Point::fromJson('{"type": "LineString","coordinates":[[1,1],[2,2]]}'); 77 | } 78 | 79 | public function testJsonSerialize() 80 | { 81 | $point = new Point(1.2, 3.4); 82 | 83 | $this->assertInstanceOf(\GeoJson\Geometry\Point::class, $point->jsonSerialize()); 84 | $this->assertSame('{"type":"Point","coordinates":[3.4,1.2]}', json_encode($point)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/Unit/Types/PolygonTest.php: -------------------------------------------------------------------------------- 1 | polygon = new Polygon([$collection], 4326); 24 | } 25 | 26 | public function testFromWKT() 27 | { 28 | $polygon = Polygon::fromWKT('POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))', 4326); 29 | $this->assertInstanceOf(Polygon::class, $polygon); 30 | 31 | $this->assertEquals(2, $polygon->count()); 32 | } 33 | 34 | public function testToWKT() 35 | { 36 | $this->assertEquals('POLYGON((0 0,1 0,1 1,0 1,0 0))', $this->polygon->toWKT()); 37 | } 38 | 39 | public function testFromJson() 40 | { 41 | $polygon = Polygon::fromJson('{"type":"Polygon","coordinates":[[[1,1],[2,1],[2,2],[1,2],[1,1]],[[1.2,1.2],[1.6,1.2],[1.6,1.8],[1.2,1.8],[1.2,1.2]]]}'); 42 | $this->assertInstanceOf(Polygon::class, $polygon); 43 | $polygonLineStrings = $polygon->getGeometries(); 44 | $this->assertEquals(2, count($polygonLineStrings)); 45 | $this->assertEquals(new Point(1, 1), $polygonLineStrings[0][0]); 46 | $this->assertEquals(new Point(1, 2), $polygonLineStrings[0][1]); 47 | $this->assertEquals(new Point(2, 2), $polygonLineStrings[0][2]); 48 | $this->assertEquals(new Point(2, 1), $polygonLineStrings[0][3]); 49 | $this->assertEquals(new Point(1, 1), $polygonLineStrings[0][4]); 50 | $this->assertEquals(new Point(1.2, 1.2), $polygonLineStrings[1][0]); 51 | $this->assertEquals(new Point(1.2, 1.6), $polygonLineStrings[1][1]); 52 | $this->assertEquals(new Point(1.8, 1.6), $polygonLineStrings[1][2]); 53 | $this->assertEquals(new Point(1.8, 1.2), $polygonLineStrings[1][3]); 54 | $this->assertEquals(new Point(1.2, 1.2), $polygonLineStrings[1][4]); 55 | } 56 | 57 | public function testInvalidGeoJsonException() 58 | { 59 | $this->assertException( 60 | \Grimzy\LaravelMysqlSpatial\Exceptions\InvalidGeoJsonException::class, 61 | 'Expected GeoJson\Geometry\Polygon, got GeoJson\Geometry\Point' 62 | ); 63 | Polygon::fromJson('{"type":"Point","coordinates":[3.4,1.2]}'); 64 | } 65 | 66 | public function testJsonSerialize() 67 | { 68 | $this->assertInstanceOf(\GeoJson\Geometry\Polygon::class, $this->polygon->jsonSerialize()); 69 | $this->assertSame( 70 | '{"type":"Polygon","coordinates":[[[0,0],[1,0],[1,1],[0,1],[0,0]]]}', 71 | json_encode($this->polygon) 72 | ); 73 | } 74 | } 75 | --------------------------------------------------------------------------------