Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Geometry and Custom SRID #76

Merged
merged 15 commits into from
Jul 15, 2017
58 changes: 50 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Laravel postgis extension

* Work with geometry classes instead of arrays. (`$myModel->myPoint = new Point(1,2)`)
* Adds helpers in migrations. (`$table->polygon('myColumn')`)

### Future plans

* Geometry functions on the geometry classes (contains(), equals(), distance(), etc… (HELP!))

## Versions
Expand All @@ -22,7 +22,7 @@ Laravel postgis extension

## Installation

composer require phaza/laravel-postgis
composer require phaza/laravel-postgis

Next add the DatabaseServiceProvider to your `config/app.php` file.

Expand Down Expand Up @@ -70,8 +70,11 @@ class CreateLocationsTable extends Migration {
$table->increments('id');
$table->string('name');
$table->string('address')->unique();
$table->point('location');
$table->polygon('polygon');
$table->point('location'); // GEOGRAPHY POINT column with SRID of 4326 (these are the default values).
$table->point('location2', 'GEOGRAPHY', 4326); // GEOGRAPHY POINT column with SRID of 4326 with optional parameters.
$table->point('location3', 'GEOMETRY', 27700); // GEOMETRY column with SRID of 27700.
$table->polygon('polygon'); // GEOGRAPHY POLYGON column with SRID of 4326.
$table->polygon('polygon2', 'GEOMETRY', 27700); // GEOMETRY POLYGON column with SRID of 27700.
$table->timestamps();
});
}
Expand Down Expand Up @@ -109,7 +112,7 @@ other methods:
All models which are to be PostGis enabled **must** use the *PostgisTrait*.

You must also define an array called `$postgisFields` which defines
what attributes/columns on your model are to be considered geometry objects.
what attributes/columns on your model are to be considered geometry objects. By default, all attributes are of type `geography`. If you want to use `geometry` with a custom SRID, you have to define an array called `$postgisTypes`. The keys of this assoc array must match the entries in `$postgisFields` (all missing keys default to `geography`), the values are assoc arrays, too. They must have two keys: `geomtype` which is either `geography` or `geometry` and `srid` which is the desired SRID. **Note**: Custom SRID is only supported for `geometry`, not `geography`.

```PHP
use Illuminate\Database\Eloquent\Model;
Expand All @@ -127,23 +130,62 @@ class Location extends Model

protected $postgisFields = [
'location',
'location2',
'location3',
'polygon',
'polygon2'
];


protected $postgisTypes = [
'location' => [
'geomtype' => 'geography',
'srid' => 4326
],
'location2' => [
'geomtype' => 'geography',
'srid' => 4326
],
'location3' => [
'geomtype' => 'geometry',
'srid' => 27700
],
'polygon' => [
'geomtype' => 'geography',
'srid' => 4326
],
'polygon2' => [
'geomtype' => 'geometry',
'srid' => 27700
]
]
}

$linestring = new LineString(
[
new Point(0, 0),
new Point(0, 1),
new Point(1, 1),
new Point(1, 0),
new Point(0, 0)
]
);

$location1 = new Location();
$location1->name = 'Googleplex';
$location1->address = '1600 Amphitheatre Pkwy Mountain View, CA 94043';
$location1->location = new Point(37.422009, -122.084047);
$location1->location2 = new Point(37.422009, -122.084047);
$location1->location3 = new Point(37.422009, -122.084047);
$location1->polygon = new Polygon([$linestring]);
$location1->polygon2 = new Polygon([$linestring]);
$location1->save();

$location2 = Location::first();
$location2->location instanceof Point // true
```

Available geometry classes:

* Point
* MultiPoint
* LineString
Expand Down
50 changes: 45 additions & 5 deletions src/Eloquent/PostgisTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Arr;
use Phaza\LaravelPostgis\Exceptions\PostgisFieldsNotDefinedException;
use Phaza\LaravelPostgis\Exceptions\PostgisTypesMalformedException;
use Phaza\LaravelPostgis\Exceptions\UnsupportedGeomtypeException;
use Phaza\LaravelPostgis\Geometries\Geometry;
use Phaza\LaravelPostgis\Geometries\GeometryInterface;
use Phaza\LaravelPostgis\Schema\Grammars\PostgisGrammar;

trait PostgisTrait
{
Expand All @@ -24,12 +27,22 @@ public function newEloquentBuilder($query)
protected function performInsert(EloquentBuilder $query, array $options = [])
{
foreach ($this->attributes as $key => $value) {
if ($value instanceof GeometryInterface && ! $value instanceof GeometryCollection) {
if ($value instanceof GeometryInterface) {
$this->geometries[$key] = $value; //Preserve the geometry objects prior to the insert
$this->attributes[$key] = $this->getConnection()->raw(sprintf("public.ST_GeogFromText('%s')", $value->toWKT()));
} else if ($value instanceof GeometryInterface && $value instanceof GeometryCollection) {
$this->geometries[$key] = $value; //Preserve the geometry objects prior to the insert
$this->attributes[$key] = $this->getConnection()->raw(sprintf("public.ST_GeomFromText('%s', 4326)", $value->toWKT()));
if (! $value instanceof GeometryCollection) {
$attrs = $this->getPostgisType($key);
switch (strtoupper($attrs['geomtype'])) {
case 'GEOMETRY':
$this->attributes[$key] = $this->getConnection()->raw(sprintf("public.ST_GeomFromText('%s', '%d')", $value->toWKT(), $attrs['srid']));
break;
case 'GEOGRAPHY':
default:
$this->attributes[$key] = $this->getConnection()->raw(sprintf("public.ST_GeogFromText('%s')", $value->toWKT()));
break;
}
} else {
$this->attributes[$key] = $this->getConnection()->raw(sprintf("public.ST_GeomFromText('%s', 4326)", $value->toWKT()));
}
}
}

Expand All @@ -55,6 +68,33 @@ public function setRawAttributes(array $attributes, $sync = false)
return parent::setRawAttributes($attributes, $sync);
}

public function getPostgisType($key)
{
$default = [
'geomtype' => 'geography',
'srid' => 4326
];

if (property_exists($this, 'postgisTypes')) {
if (Arr::isAssoc($this->postgisTypes)) {
if(!array_key_exists($key, $this->postgisTypes)) {
return $default;
}
$column = $this->postgisTypes[$key];
if (isset($column['geomtype']) && in_array(strtoupper($column['geomtype']), PostgisGrammar::$allowed_geom_types)) {
return $column;
} else {
throw new UnsupportedGeomtypeException('Unsupported GeometryType in $postgisTypes for key ' . $key . ' in ' . __CLASS__);
}
} else {
throw new PostgisTypesMalformedException('$postgisTypes in ' . __CLASS__ . ' has to be an assoc array');
}
}

// Return default geography if postgisTypes does not exist (for backward compatibility)
return $default;
}

public function getPostgisFields()
{
if (property_exists($this, 'postgisFields')) {
Expand Down
7 changes: 7 additions & 0 deletions src/Exceptions/PostgisTypesMalformedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php namespace Phaza\LaravelPostgis\Exceptions;

use RuntimeException;

class PostgisTypesMalformedException extends RuntimeException
{
}
7 changes: 7 additions & 0 deletions src/Exceptions/UnsupportedGeomtypeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php namespace Phaza\LaravelPostgis\Exceptions;

use RuntimeException;

class UnsupportedGeomtypeException extends RuntimeException
{
}
1 change: 1 addition & 0 deletions src/PostgisConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public function __construct($pdo, $database = '', $tablePrefix = '', array $conf

// Prevent geography type fields from throwing a 'type not found' error.
$this->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('geography', 'string');
$this->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('geometry', 'string');
}

/**
Expand Down
39 changes: 25 additions & 14 deletions src/Schema/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ class Blueprint extends \Bosnadev\Database\Schema\Blueprint
* @param $column
* @return \Illuminate\Support\Fluent
*/
public function point($column)
public function point($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('point', $column);
return $this->addColumn('point', $column, compact('geomtype', 'srid'));
}

/**
Expand All @@ -19,9 +19,9 @@ public function point($column)
* @param $column
* @return \Illuminate\Support\Fluent
*/
public function multipoint($column)
public function multipoint($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('multipoint', $column);
return $this->addColumn('multipoint', $column, compact('geomtype', 'srid'));
}

/**
Expand All @@ -30,9 +30,9 @@ public function multipoint($column)
* @param $column
* @return \Illuminate\Support\Fluent
*/
public function polygon($column)
public function polygon($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('polygon', $column);
return $this->addColumn('polygon', $column, compact('geomtype', 'srid'));
}

/**
Expand All @@ -41,9 +41,9 @@ public function polygon($column)
* @param $column
* @return \Illuminate\Support\Fluent
*/
public function multipolygon($column)
public function multipolygon($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('multipolygon', $column);
return $this->addColumn('multipolygon', $column, compact('geomtype', 'srid'));
}

/**
Expand All @@ -52,9 +52,9 @@ public function multipolygon($column)
* @param $column
* @return \Illuminate\Support\Fluent
*/
public function linestring($column)
public function linestring($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('linestring', $column);
return $this->addColumn('linestring', $column, compact('geomtype', 'srid'));
}

/**
Expand All @@ -63,9 +63,9 @@ public function linestring($column)
* @param $column
* @return \Illuminate\Support\Fluent
*/
public function multilinestring($column)
public function multilinestring($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('multilinestring', $column);
return $this->addColumn('multilinestring', $column, compact('geomtype', 'srid'));
}

/**
Expand All @@ -74,9 +74,20 @@ public function multilinestring($column)
* @param string $column
* @return \Illuminate\Support\Fluent
*/
public function geography($column)
public function geography($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('geography', $column);
return $this->addColumn('geography', $column, compact('geomtype', 'srid'));
}

/**
* Add a geometry column on the table
*
* @param string $column
* @return \Illuminate\Support\Fluent
*/
public function geometry($column, $geomtype = 'GEOGRAPHY', $srid = '4326')
{
return $this->addColumn('geometry', $column, compact('geomtype', 'srid'));
}

/**
Expand Down
Loading