-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #251 from wri/feat/TM-920-greenhouse-point-api
[TM-920] Store PointGeometry in the GH API bulk upload geometry endpoint.
- Loading branch information
Showing
18 changed files
with
596 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<?php | ||
|
||
namespace App\Http\Requests\V2\Geometry; | ||
|
||
use App\Models\V2\Sites\Site; | ||
use Illuminate\Foundation\Http\FormRequest; | ||
use Illuminate\Support\Facades\Validator; | ||
use Illuminate\Validation\ValidationException; | ||
|
||
class StoreGeometryRequest extends FormRequest | ||
{ | ||
protected array $geometries; | ||
|
||
protected array $siteIds; | ||
|
||
protected array $sites; | ||
|
||
public function authorize(): bool | ||
{ | ||
return true; | ||
} | ||
|
||
public function rules(): array | ||
{ | ||
return [ | ||
'geometries' => 'required|array', | ||
'geometries.*.features' => 'required|array|min:1', | ||
'geometries.*.features.*.geometry.type' => 'required|string|in:Point,Polygon', | ||
]; | ||
} | ||
|
||
public function getGeometries(): array | ||
{ | ||
if (! empty($this->geometries)) { | ||
return $this->geometries; | ||
} | ||
|
||
return $this->geometries = $this->input('geometries'); | ||
} | ||
|
||
public function getSiteIds(): array | ||
{ | ||
if (! empty($this->siteIds)) { | ||
return $this->siteIds; | ||
} | ||
|
||
return $this->siteIds = collect( | ||
data_get($this->getGeometries(), '*.features.*.properties.site_id') | ||
)->unique()->filter()->toArray(); | ||
} | ||
|
||
public function getSites(): array | ||
{ | ||
if (! empty($this->sites)) { | ||
return $this->sites; | ||
} | ||
|
||
return $this->sites = Site::whereIn('uuid', $this->getSiteIds())->get()->all(); | ||
} | ||
|
||
/** | ||
* @throws ValidationException | ||
*/ | ||
public function validateGeometries(): void | ||
{ | ||
// Make sure the data is coherent. Since we accept both Polygons and Points on this request, we have to | ||
// validate each geometry individually, rather than in the rules above | ||
foreach ($this->getGeometries() as $geometry) { | ||
$type = data_get($geometry, 'features.0.geometry.type'); | ||
if ($type == 'Polygon') { | ||
// Require that we only have one geometry and that it has a site_id specified | ||
Validator::make($geometry, [ | ||
'features' => 'required|array|size:1', | ||
'features.0.properties.site_id' => 'required|string', | ||
])->validate(); | ||
|
||
// This is guaranteed to be Point given the rules specified in rules() | ||
} else { | ||
// Require that all geometries in the collection are valid points, include estimated area, and that the | ||
// collection has exactly one unique site id. | ||
$siteIds = collect(data_get($geometry, 'features.*.properties.site_id')) | ||
->unique()->filter()->toArray(); | ||
Validator::make(['geometry' => $geometry, 'site_ids' => $siteIds], [ | ||
'geometry.features.*.geometry.type' => 'required|string|in:Point', | ||
'geometry.features.*.geometry.coordinates' => 'required|array|size:2', | ||
'geometry.features.*.properties.est_area' => 'required|numeric|min:1', | ||
'site_ids' => 'required|array|size:1', | ||
])->validate(); | ||
} | ||
} | ||
|
||
// Structure this as a validation exception just to make the return shape of this endpoint consistent. | ||
Validator::make(['num_sites' => count($this->getSites()), 'num_site_ids' => count($this->getSiteIds())], [ | ||
'num_sites' => 'same:num_site_ids', | ||
])->validate(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
namespace App\Models\Traits; | ||
|
||
/** | ||
* @method static isUuid($uuid) | ||
* @property mixed $uuid | ||
*/ | ||
trait HasGeometry | ||
{ | ||
public static function getGeoJson(string $uuid): ?array | ||
{ | ||
$geojson_string = static::isUuid($uuid) | ||
->selectRaw('ST_AsGeoJSON(geom) as geojson_string') | ||
->first() | ||
?->geojson_string; | ||
|
||
return $geojson_string == null ? null : json_decode($geojson_string, true); | ||
} | ||
|
||
public function getGeoJsonAttribute(): array | ||
{ | ||
return self::getGeoJson($this->uuid); | ||
} | ||
|
||
public static function getGeometryType(string $uuid): ?string | ||
{ | ||
return static::isUuid($uuid) | ||
->selectRaw('ST_GeometryType(geom) as geometry_type_string') | ||
->first() | ||
?->geometry_type_string; | ||
} | ||
|
||
public function getGeometryTypeAttribute(): string | ||
{ | ||
return self::getGeometryType($this->uuid); | ||
} | ||
|
||
public static function getDbGeometry(string $uuid) | ||
{ | ||
return static::isUuid($uuid) | ||
->selectRaw('ST_Area(geom) AS area, ST_Y(ST_Centroid(geom)) AS latitude') | ||
->first(); | ||
} | ||
|
||
public function getDbGeometryAttribute() | ||
{ | ||
return self::getDbGeometry($this->uuid); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
|
||
namespace App\Models\V2; | ||
|
||
use App\Models\Traits\HasGeometry; | ||
use App\Models\Traits\HasUuid; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\HasOne; | ||
use Illuminate\Database\Eloquent\SoftDeletes; | ||
|
||
class PointGeometry extends Model | ||
{ | ||
use HasUuid; | ||
use SoftDeletes; | ||
use HasGeometry; | ||
|
||
protected $table = 'point_geometry'; | ||
|
||
protected $fillable = [ | ||
'geom', | ||
'est_area', | ||
'created_by', | ||
'last_modified_by', | ||
]; | ||
|
||
public function getRouteKeyName() | ||
{ | ||
return 'uuid'; | ||
} | ||
|
||
public function createdBy(): HasOne | ||
{ | ||
return $this->hasOne(User::class, 'id', 'created_by'); | ||
} | ||
|
||
public function lastModifiedBy(): HasOne | ||
{ | ||
return $this->hasOne(User::class, 'id', 'last_modified_by'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.