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

Custom spherical radius & SpatialTrait annotations #117

Open
wants to merge 4 commits into
base: srid
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 127 additions & 14 deletions src/Eloquent/SpatialTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,16 @@ public function getSpatialFields()
}
}

public function isColumnAllowed($geometryColumn)
/**
* Checks whether a column name exists in a SpatialTrait::$geometries
*
* @param string $geometryColumn
*
* @throws SpatialFieldsNotDefinedException
*
* @return bool
*/
public function isColumnAllowed(string $geometryColumn)
{
if (!in_array($geometryColumn, $this->getSpatialFields())) {
throw new SpatialFieldsNotDefinedException();
Expand All @@ -128,7 +137,19 @@ public function isColumnAllowed($geometryColumn)
return true;
}

public function scopeDistance($query, $geometryColumn, $geometry, $distance)
/**
* Queries a geometry column to have less than or equal distance to a geometry object on a flat surface
*
* @param EloquentBuilder $query
* @param string $geometryColumn
* @param Geometry $geometry
* @param float $distance
*
* @throws SpatialFieldsNotDefinedException
*
* @return EloquentBuilder
*/
public function scopeDistance(EloquentBuilder $query, string $geometryColumn, Geometry $geometry, float $distance)
{
$this->isColumnAllowed($geometryColumn);

Expand All @@ -140,7 +161,19 @@ public function scopeDistance($query, $geometryColumn, $geometry, $distance)
return $query;
}

public function scopeDistanceExcludingSelf($query, $geometryColumn, $geometry, $distance)
/**
* Queries a geometry column to have less than or equal distance to a geometry object on a flat surface, excluding the geometry object from result set
*
* @param EloquentBuilder $query
* @param string $geometryColumn
* @param Geometry $geometry
* @param float $distance
*
* @throws SpatialFieldsNotDefinedException
*
* @return EloquentBuilder
*/
public function scopeDistanceExcludingSelf(EloquentBuilder $query, string $geometryColumn, Geometry $geometry, float $distance)
{
$this->isColumnAllowed($geometryColumn);

Expand All @@ -153,7 +186,18 @@ public function scopeDistanceExcludingSelf($query, $geometryColumn, $geometry, $
return $query;
}

public function scopeDistanceValue($query, $geometryColumn, $geometry)
/**
* Queries a table for distance between a geometry column and a geometry object on a flat surface
*
* @param EloquentBuilder $query
* @param string $geometryColumn
* @param Geometry $geometry
*
* @throws SpatialFieldsNotDefinedException
*
* @return EloquentBuilder
*/
public function scopeDistanceValue(EloquentBuilder $query, string $geometryColumn, Geometry $geometry)
{
$this->isColumnAllowed($geometryColumn);

Expand All @@ -168,32 +212,72 @@ public function scopeDistanceValue($query, $geometryColumn, $geometry)
]);
}

public function scopeDistanceSphere($query, $geometryColumn, $geometry, $distance)
/**
* Queries a geometry column to have less than or equal distance to a geometry object on a sphere
*
* @param EloquentBuilder $query
* @param string $geometryColumn
* @param Geometry $geometry
* @param float $distance
* @param float $radius
*
* @throws SpatialFieldsNotDefinedException
*
* @return EloquentBuilder
*/
public function scopeDistanceSphere(EloquentBuilder $query, string $geometryColumn, Geometry $geometry, float $distance, float $radius = 6371008.7714150598325213221998779)
{
$this->isColumnAllowed($geometryColumn);

$query->whereRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?)) <= ?", [
$query->whereRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?), ?) <= ?", [
$geometry->toWkt(),
$radius,
$distance,
]);

return $query;
}

public function scopeDistanceSphereExcludingSelf($query, $geometryColumn, $geometry, $distance)
/**
* Queries a geometry column to have less than or equal distance to a geometry object on a sphere, excluding the geometry object from result set
*
* @param EloquentBuilder $query
* @param string $geometryColumn
* @param Geometry $geometry
* @param float $distance
* @param float $radius
*
* @throws SpatialFieldsNotDefinedException
*
* @return EloquentBuilder
*/
public function scopeDistanceSphereExcludingSelf(EloquentBuilder $query, string $geometryColumn, Geometry $geometry, float $distance, float $radius = 6371008.7714150598325213221998779)
{
$this->isColumnAllowed($geometryColumn);

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

$query->whereRaw("st_distance_sphere($geometryColumn, ST_GeomFromText(?)) != 0", [
$query->whereRaw("st_distance_sphere($geometryColumn, ST_GeomFromText(?), ?) != 0", [
$geometry->toWkt(),
$radius,
]);

return $query;
}

public function scopeDistanceSphereValue($query, $geometryColumn, $geometry)
/**
* Queries a table for distance between a geometry column and a geometry object on a flat surface
*
* @param EloquentBuilder $query
* @param string $geometryColumn
* @param Geometry $geometry
* @param float $radius
*
* @throws SpatialFieldsNotDefinedException
*
* @return EloquentBuilder
*/
public function scopeDistanceSphereValue(EloquentBuilder $query, string $geometryColumn, Geometry $geometry, float $radius = 6371008.7714150598325213221998779)
{
$this->isColumnAllowed($geometryColumn);

Expand All @@ -202,12 +286,26 @@ public function scopeDistanceSphereValue($query, $geometryColumn, $geometry)
if (!$columns) {
$query->select('*');
}
$query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?)) as distance", [
$query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?), ?) as distance", [
$geometry->toWkt(),
$radius,
]);
}

public function scopeComparison($query, $geometryColumn, $geometry, $relationship)
/**
* Description of `scopeComparison`
*
* @param EloquentBuilder $query
* @param string $geometryColumn
* @param Geometry $geometry
* @param string $relationship
*
* @throws SpatialFieldsNotDefinedException
* @throws UnknownSpatialRelationFunction
*
* @return EloquentBuilder
*/
public function scopeComparison(EloquentBuilder $query, string $geometryColumn, Geometry $geometry, string $relationship)
{
$this->isColumnAllowed($geometryColumn);

Expand Down Expand Up @@ -279,11 +377,26 @@ public function scopeOrderBySpatial($query, $geometryColumn, $geometry, $orderFu

public function scopeOrderByDistance($query, $geometryColumn, $geometry, $direction = 'asc')
{
return $this->scopeOrderBySpatial($query, $geometryColumn, $geometry, 'distance', $direction);
$this->isColumnAllowed($geometryColumn);

$query->orderByRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) {$direction}", [
$geometry->toWkt(),
]);

return $query;
// return $this->scopeOrderBySpatial($query, $geometryColumn, $geometry, 'distance', $direction);
}

public function scopeOrderByDistanceSphere($query, $geometryColumn, $geometry, $direction = 'asc')
public function scopeOrderByDistanceSphere($query, $geometryColumn, $geometry, $direction = 'asc', float $radius = 6371008.7714150598325213221998779)
{
return $this->scopeOrderBySpatial($query, $geometryColumn, $geometry, 'distance_sphere', $direction);
$this->isColumnAllowed($geometryColumn);

$query->orderByRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?), ?) {$direction}", [
$geometry->toWkt(),
$radius,
]);

return $query;
// return $this->scopeOrderBySpatial($query, $geometryColumn, $geometry, 'distance_sphere', $direction);
}
}
16 changes: 8 additions & 8 deletions src/Types/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,41 @@ class Factory implements \GeoIO\Factory
{
public function createPoint($dimension, array $coordinates, $srid = null)
{
return new Point($coordinates['y'], $coordinates['x']);
return new Point($coordinates['y'], $coordinates['x'], $srid);
}

public function createLineString($dimension, array $points, $srid = null)
{
return new LineString($points);
return new LineString($points, $srid);
}

public function createLinearRing($dimension, array $points, $srid = null)
{
return new LineString($points);
return new LineString($points, $srid);
}

public function createPolygon($dimension, array $lineStrings, $srid = null)
{
return new Polygon($lineStrings);
return new Polygon($lineStrings, $srid);
}

public function createMultiPoint($dimension, array $points, $srid = null)
{
return new MultiPoint($points);
return new MultiPoint($points, $srid);
}

public function createMultiLineString($dimension, array $lineStrings, $srid = null)
{
return new MultiLineString($lineStrings);
return new MultiLineString($lineStrings, $srid);
}

public function createMultiPolygon($dimension, array $polygons, $srid = null)
{
return new MultiPolygon($polygons);
return new MultiPolygon($polygons, $srid);
}

public function createGeometryCollection($dimension, array $geometries, $srid = null)
{
return new GeometryCollection($geometries);
return new GeometryCollection($geometries, $srid);
}
}
28 changes: 26 additions & 2 deletions src/Types/Geometry.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ abstract class Geometry implements GeometryInterface, Jsonable, \JsonSerializabl
7 => GeometryCollection::class,
];

protected $srid = 0;

public static function getWKTArgument($value)
{
$left = strpos($value, '(');
Expand Down Expand Up @@ -54,8 +56,25 @@ public static function getWKTClass($value)

public static function fromWKB($wkb)
{
// mysql adds 4 NUL bytes at the start of the binary
$wkb = substr($wkb, 4);
// mysql adds 4 bytes at the start of the binary as SRID
// $srid = @unpack('V', substr($wkb, 0, 4))[1];
// $wkb = substr($wkb, 4);

$bom = unpack('C', substr($wkb, 4, 5));
$formatter = $bom ? 'V' : 'N';
$srid = unpack($formatter, substr($wkb, 0, 4))[1];
$type = unpack($formatter, substr($wkb, 5, 9))[1];
$wkb = substr($wkb, 9);
if ($srid)
{
$type |= Parser::MASK_SRID;
$wkb = pack('C', $bom) . pack($formatter, $type) . pack($formatter, $srid) . $wkb;
}
else
{
$wkb = pack('C', $bom) . pack($formatter, $type) . $wkb;
}

$parser = new Parser(new Factory());

return $parser->parse($wkb);
Expand Down Expand Up @@ -91,4 +110,9 @@ public function toJson($options = 0)
{
return json_encode($this, $options);
}

public function getSRID()
{
return $this->srid;
}
}
3 changes: 2 additions & 1 deletion src/Types/GeometryCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class GeometryCollection extends Geometry implements IteratorAggregate, ArrayAcc
*
* @throws InvalidArgumentException
*/
public function __construct(array $geometries)
public function __construct(array $geometries, $srid = null)
{
$validated = array_filter($geometries, function ($value) {
return $value instanceof GeometryInterface;
Expand All @@ -37,6 +37,7 @@ public function __construct(array $geometries)
}

$this->items = $geometries;
$this->srid = $srid;
}

public function getGeometries()
Expand Down
4 changes: 2 additions & 2 deletions src/Types/MultiLineString.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class MultiLineString extends GeometryCollection
/**
* @param LineString[] $lineStrings
*/
public function __construct(array $lineStrings)
public function __construct(array $lineStrings, $srid = null)
{
if (count($lineStrings) < 1) {
throw new InvalidArgumentException('$lineStrings must contain at least one entry');
Expand All @@ -26,7 +26,7 @@ public function __construct(array $lineStrings)
throw new InvalidArgumentException('$lineStrings must be an array of LineString');
}

parent::__construct($lineStrings);
parent::__construct($lineStrings, $srid);
}

public function getLineStrings()
Expand Down
4 changes: 2 additions & 2 deletions src/Types/MultiPolygon.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class MultiPolygon extends GeometryCollection
/**
* @param Polygon[] $polygons
*/
public function __construct(array $polygons)
public function __construct(array $polygons, $srid = null)
{
$validated = array_filter($polygons, function ($value) {
return $value instanceof Polygon;
Expand All @@ -21,7 +21,7 @@ public function __construct(array $polygons)
if (count($polygons) !== count($validated)) {
throw new InvalidArgumentException('$polygons must be an array of Polygon');
}
parent::__construct($polygons);
parent::__construct($polygons, $srid);
}

public function toWKT()
Expand Down
3 changes: 2 additions & 1 deletion src/Types/Point.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ class Point extends Geometry

protected $lng;

public function __construct($lat, $lng)
public function __construct($lat, $lng, $srid = null)
{
$this->lat = (float) $lat;
$this->lng = (float) $lng;
$this->srid = (int) $srid;
}

public function getLat()
Expand Down
4 changes: 2 additions & 2 deletions src/Types/PointCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ abstract class PointCollection extends GeometryCollection
/**
* @param Point[] $points
*/
public function __construct(array $points)
public function __construct(array $points, $srid = null)
{
if (count($points) < 2) {
throw new InvalidArgumentException('$points must contain at least two entries');
Expand All @@ -24,7 +24,7 @@ public function __construct(array $points)
throw new InvalidArgumentException('$points must be an array of Points');
}

parent::__construct($points);
parent::__construct($points, $srid);
}

public function toPairList()
Expand Down