diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php index c346905f5..cb387fea0 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundCreateGeometryController.php @@ -20,8 +20,8 @@ class TerrafundCreateGeometryController extends Controller public function processGeometry(string $uuid) { $geometry = PolygonGeometry::where('uuid', $uuid) - ->select(DB::raw('ST_AsGeoJSON(geom) AS geojson')) - ->first(); + ->select(DB::raw('ST_AsGeoJSON(geom) AS geojson')) + ->first(); $geojson = $geometry->geojson; @@ -35,14 +35,14 @@ public function processGeometry(string $uuid) public function storeGeometry(Request $request) { $request->validate([ - 'geometry' => 'required|json', + 'geometry' => 'required|json', ]); $geometry = json_decode($request->input('geometry')); $geom = DB::raw("ST_GeomFromGeoJSON('" . json_encode($geometry) . "')"); $polygonGeometry = PolygonGeometry::create([ - 'geom' => $geom, + 'geom' => $geom, ]); return response()->json(['uuid' => $polygonGeometry->uuid], 200); @@ -85,7 +85,7 @@ private function insertSinglePolygon(array $geometry, int $srid) $areaHectares = $areaSqMeters / 10000; $polygonGeometry = PolygonGeometry::create([ - 'geom' => $geom, + 'geom' => $geom, ]); return ['uuid' => $polygonGeometry->uuid, 'id' => $polygonGeometry->id, 'area' => $areaHectares]; @@ -96,7 +96,7 @@ private function insertSinglePolygon(array $geometry, int $srid) } } - public function insertGeojsonToDB(string $geojsonFilename) + public function insertGeojsonToDB(string $geojsonFilename, ?string $site_id = null) { $srid = 4326; $geojsonData = Storage::get("public/geojson_files/{$geojsonFilename}"); @@ -106,6 +106,9 @@ public function insertGeojsonToDB(string $geojsonFilename) } $uuids = []; foreach ($geojson['features'] as $feature) { + if ($site_id !== null) { + $feature['properties']['site_id'] = $site_id; + } if ($feature['geometry']['type'] === 'Polygon') { if (! $this->validatePolygonBounds($feature['geometry'])) { return ['error' => 'Invalid polygon bounds']; @@ -114,7 +117,7 @@ public function insertGeojsonToDB(string $geojsonFilename) $uuids[] = $data['uuid']; $returnSite = $this->insertSitePolygon($data['uuid'], $feature['properties'], $data['area']); if ($returnSite) { - Log::info($returnSite) ; + Log::info($returnSite); } } elseif ($feature['geometry']['type'] === 'MultiPolygon') { foreach ($feature['geometry']['coordinates'] as $polygon) { @@ -126,7 +129,7 @@ public function insertGeojsonToDB(string $geojsonFilename) $uuids[] = $data['uuid']; $returnSite = $this->insertSitePolygon($data['uuid'], $feature['properties'], $data['area']); if ($returnSite) { - Log::info($returnSite) ; + Log::info($returnSite); } } } @@ -228,7 +231,7 @@ private function insertSitePolygon(string $polygonUuid, array $properties, float $sitePolygon->target_sys = $properties['target_sys'] ?? null; $sitePolygon->distr = $properties['distr'] ?? null; $sitePolygon->num_trees = $properties['num_trees'] ?? null; - $sitePolygon->est_area = $area ?? null; + $sitePolygon->calc_area = $area ?? null; $sitePolygon->save(); return null; @@ -261,6 +264,7 @@ public function getGeometryProperties(string $geojsonFilename) public function uploadKMLFile(Request $request) { if ($request->hasFile('file')) { + $site_id = $request->input('uuid'); $kmlfile = $request->file('file'); $directory = storage_path('app/public/kml_files'); if (! file_exists($directory)) { @@ -278,7 +282,7 @@ public function uploadKMLFile(Request $request) return response()->json(['error' => 'Failed to convert KML to GeoJSON', 'message' => $process->getErrorOutput()], 500); } - $uuid = $this->insertGeojsonToDB($geojsonFilename); + $uuid = $this->insertGeojsonToDB($geojsonFilename, $site_id); if (isset($uuid['error'])) { return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); } @@ -310,6 +314,7 @@ public function uploadShapefile(Request $request) { Log::debug('Upload Shape file data', ['request' => $request->all()]); if ($request->hasFile('file')) { + $site_id = $request->input('uuid'); $file = $request->file('file'); if ($file->getClientOriginalExtension() !== 'zip') { return response()->json(['error' => 'Only ZIP files are allowed'], 400); @@ -335,7 +340,7 @@ public function uploadShapefile(Request $request) return response()->json(['error' => 'Failed to convert Shapefile to GeoJSON', 'message' => $process->getErrorOutput()], 500); } - $uuid = $this->insertGeojsonToDB($geojsonFilename); + $uuid = $this->insertGeojsonToDB($geojsonFilename, $site_id); if (isset($uuid['error'])) { return response()->json(['error' => 'Geometry not inserted into DB', 'message' => $uuid['error']], 500); } @@ -463,11 +468,11 @@ public function validatePolygonSize(Request $request) $insertionSuccess = $this->insertCriteriaSite($uuid, $SIZE_CRITERIA_ID, $valid); return response()->json([ - 'area_hectares' => $areaSqMeters / 10000, // Convert to hectares - 'area_sqmeters' => $areaSqMeters, - 'geometry_id' => $geometry->id, - 'insertion_success' => $insertionSuccess, - 'valid' => $valid, + 'area_hectares' => $areaSqMeters / 10000, // Convert to hectares + 'area_sqmeters' => $areaSqMeters, + 'geometry_id' => $geometry->id, + 'insertion_success' => $insertionSuccess, + 'valid' => $valid, ], 200); } @@ -504,8 +509,8 @@ public function checkWithinCountry(Request $request) } $intersectionData = WorldCountryGeneralized::where('iso', $countryIso) - ->selectRaw('world_countries_generalized.country AS country, ST_Area(ST_Intersection(world_countries_generalized.geometry, (SELECT geom FROM polygon_geometry WHERE uuid = ?))) AS area', [$polygonUuid]) - ->first(); + ->selectRaw('world_countries_generalized.country AS country, ST_Area(ST_Intersection(world_countries_generalized.geometry, (SELECT geom FROM polygon_geometry WHERE uuid = ?))) AS area', [$polygonUuid]) + ->first(); $intersectionArea = $intersectionData->area; $countryName = $intersectionData->country; @@ -577,9 +582,9 @@ public function getCriteriaData(Request $request) $criteriaId = $criteria->criteria_id; $valid = CriteriaSite::where(['polygon_id' => $uuid, 'criteria_id' => $criteriaId])->select('valid')->first()?->valid; $criteriaList[] = [ - 'criteria_id' => $criteriaId, - 'latest_created_at' => $criteria->latest_created_at, - 'valid' => $valid, + 'criteria_id' => $criteriaId, + 'latest_created_at' => $criteria->latest_created_at, + 'valid' => $valid, ]; } @@ -589,6 +594,7 @@ public function getCriteriaData(Request $request) public function uploadGeoJSONFile(Request $request) { if ($request->hasFile('file')) { + $site_id = $request->input('uuid'); $file = $request->file('file'); $directory = storage_path('app/public/geojson_files'); if (! file_exists($directory)) { @@ -596,7 +602,7 @@ public function uploadGeoJSONFile(Request $request) } $filename = uniqid('geojson_file_') . '.' . $file->getClientOriginalExtension(); $file->move($directory, $filename); - $uuid = $this->insertGeojsonToDB($filename); + $uuid = $this->insertGeojsonToDB($filename, $site_id); if (is_array($uuid) && isset($uuid['error'])) { return response()->json(['error' => 'Failed to insert GeoJSON data into the database', 'message' => $uuid['error']], 500); } @@ -611,24 +617,24 @@ public function validateOverlapping(Request $request) { $uuid = $request->input('uuid'); $sitePolygon = SitePolygon::where('poly_id', $uuid) - ->first(); + ->first(); if (! $sitePolygon) { return response()->json(['error' => 'Site polygon not found for the given polygon ID'], 200); } $projectId = $sitePolygon->project_id; - if(! $projectId) { + if (! $projectId) { return response()->json(['error' => 'Project ID not found for the given polygon ID'], 200); } $relatedPolyIds = SitePolygon::where('project_id', $projectId) - ->where('poly_id', '!=', $uuid) - ->pluck('poly_id'); + ->where('poly_id', '!=', $uuid) + ->pluck('poly_id'); $intersects = PolygonGeometry::whereIn('uuid', $relatedPolyIds) - ->selectRaw('ST_Intersects(geom, (SELECT geom FROM polygon_geometry WHERE uuid = ?)) as intersects', [$uuid]) - ->get() - ->pluck('intersects'); + ->selectRaw('ST_Intersects(geom, (SELECT geom FROM polygon_geometry WHERE uuid = ?)) as intersects', [$uuid]) + ->get() + ->pluck('intersects'); $intersects = in_array(1, $intersects->toArray()); $valid = ! $intersects; @@ -642,7 +648,7 @@ public function validateEstimatedArea(Request $request) { $uuid = $request->input('uuid'); $sitePolygon = SitePolygon::where('poly_id', $uuid) - ->first(); + ->first(); if (! $sitePolygon) { return response()->json(['error' => 'Site polygon not found for the given polygon ID'], 200); @@ -651,10 +657,10 @@ public function validateEstimatedArea(Request $request) $projectId = $sitePolygon->project_id; $sumEstArea = SitePolygon::where('project_id', $projectId) - ->sum('est_area'); + ->sum('calc_area'); $project = Project::where('uuid', $projectId) - ->first(); + ->first(); if (! $project) { return response()->json(['error' => 'Project not found for the given project ID', 'projectId' => $projectId], 200); @@ -680,27 +686,27 @@ public function getPolygonsAsGeoJSON() { $limit = 2; $polygons = PolygonGeometry::select(DB::raw('ST_AsGeoJSON(geom) AS geojson')) - ->orderBy('created_at', 'desc') - ->whereNotNull('geom') - ->limit($limit) - ->get(); + ->orderBy('created_at', 'desc') + ->whereNotNull('geom') + ->limit($limit) + ->get(); $features = []; foreach ($polygons as $polygon) { $coordinates = json_decode($polygon->geojson)->coordinates; $feature = [ - 'type' => 'Feature', - 'geometry' => [ - 'type' => 'Polygon', - 'coordinates' => $coordinates, - ], - 'properties' => [], + 'type' => 'Feature', + 'geometry' => [ + 'type' => 'Polygon', + 'coordinates' => $coordinates, + ], + 'properties' => [], ]; $features[] = $feature; } $geojson = [ - 'type' => 'FeatureCollection', - 'features' => $features, + 'type' => 'FeatureCollection', + 'features' => $features, ]; // Return the GeoJSON data @@ -710,9 +716,9 @@ public function getPolygonsAsGeoJSON() public function getAllCountryNames() { $countries = WorldCountryGeneralized::select('country') - ->distinct() - ->orderBy('country') - ->pluck('country'); + ->distinct() + ->orderBy('country') + ->pluck('country'); return response()->json(['countries' => $countries]); } diff --git a/app/Http/Controllers/V2/Terrafund/TerrafundEditGeometryController.php b/app/Http/Controllers/V2/Terrafund/TerrafundEditGeometryController.php index b9c57ecf5..25e50f927 100644 --- a/app/Http/Controllers/V2/Terrafund/TerrafundEditGeometryController.php +++ b/app/Http/Controllers/V2/Terrafund/TerrafundEditGeometryController.php @@ -67,7 +67,7 @@ public function updateSitePolygon(string $uuid, Request $request) 'practice' => 'nullable|string', 'distr' => 'nullable|string', 'num_trees' => 'nullable|integer', - 'est_area' => 'nullable|numeric', + 'calc_area' => 'nullable|numeric', 'target_sys' => 'nullable|string', ]); @@ -108,7 +108,7 @@ public function createSitePolygon(string $uuid, Request $request) 'practice' => $validatedData['practice'], 'distr' => $validatedData['distr'], 'num_trees' => $validatedData['num_trees'], - 'est_area' => $areaHectares, // Assign the calculated area + 'calc_area' => $areaHectares, // Assign the calculated area 'target_sys' => $validatedData['target_sys'], ]); $sitePolygon->poly_id = $uuid; @@ -121,4 +121,59 @@ public function createSitePolygon(string $uuid, Request $request) return response()->json(['error' => 'An error occurred: ' . $e->getMessage()], 500); } } + + public function createSiteForPolygon(string $uuid, string $siteUuid, Request $request) + { + try { + if ($request->getContent() === '{}') { + $validatedData = [ + 'poly_name' => null, + 'plantstart' => null, + 'plantend' => null, + 'practice' => null, + 'distr' => null, + 'num_trees' => null, + 'target_sys' => null, + ]; + } else { + $validatedData = $request->validate([ + 'poly_name' => 'nullable|string', + 'plantstart' => 'nullable|date', + 'plantend' => 'nullable|date', + 'practice' => 'nullable|string', + 'distr' => 'nullable|string', + 'num_trees' => 'nullable|integer', + 'target_sys' => 'nullable|string', + ]); + } + $polygonGeometry = PolygonGeometry::where('uuid', $uuid)->first(); + if (! $polygonGeometry) { + return response()->json(['message' => 'No polygon geometry found for the given UUID.'], 404); + } + $areaSqDegrees = DB::selectOne('SELECT ST_Area(geom) AS area FROM polygon_geometry WHERE uuid = :uuid', ['uuid' => $uuid])->area; + $latitude = DB::selectOne('SELECT ST_Y(ST_Centroid(geom)) AS latitude FROM polygon_geometry WHERE uuid = :uuid', ['uuid' => $uuid])->latitude; + $areaSqMeters = $areaSqDegrees * pow(111320 * cos(deg2rad($latitude)), 2); + $areaHectares = $areaSqMeters / 10000; + $sitePolygon = new SitePolygon([ + 'poly_name' => $validatedData['poly_name'], + 'plantstart' => $validatedData['plantstart'], + 'plantend' => $validatedData['plantend'], + 'practice' => $validatedData['practice'], + 'distr' => $validatedData['distr'], + 'num_trees' => $validatedData['num_trees'], + 'calc_area' => $areaHectares, + 'target_sys' => $validatedData['target_sys'], + 'status' => 'Submitted', + 'site_id' => $siteUuid, + ]); + $sitePolygon->poly_id = $uuid; + $sitePolygon->uuid = Str::uuid(); + $sitePolygon->save(); + + return response()->json(['message' => 'Site polygon created successfully', 'uuid' => $sitePolygon, 'area' => $areaHectares], 201); + } catch (\Exception $e) { + // Handle other exceptions + return response()->json(['error' => 'An error occurred: ' . $e->getMessage()], 500); + } + } } diff --git a/app/Models/V2/Sites/SitePolygon.php b/app/Models/V2/Sites/SitePolygon.php index b57290ca4..748ca524f 100644 --- a/app/Models/V2/Sites/SitePolygon.php +++ b/app/Models/V2/Sites/SitePolygon.php @@ -16,22 +16,18 @@ class SitePolygon extends Model protected $table = 'site_polygon'; protected $fillable = [ - 'proj_name', - 'org_name', 'poly_id', 'poly_name', 'site_id', - 'site_name', 'project_id', - 'poly_label', 'plantstart', 'plantend', 'practice', 'target_sys', 'distr', 'num_trees', - 'est_area', - 'country', + 'calc_area', + 'status', ]; public function polygonGeometry() diff --git a/database/migrations/2024_04_2_121238_create_sites_polygons.php b/database/migrations/2024_04_2_121238_create_sites_polygons.php index 8b5a227e1..782393534 100644 --- a/database/migrations/2024_04_2_121238_create_sites_polygons.php +++ b/database/migrations/2024_04_2_121238_create_sites_polygons.php @@ -18,22 +18,16 @@ public function up() $table->id(); $table->uuid('uuid')->unique(); $table->string('project_id')->nullable(); - $table->string('proj_name')->nullable(); $table->string('site_id')->nullable(); - $table->string('site_name')->nullable(); - $table->string('org_name')->nullable(); $table->string('poly_id')->nullable(); $table->string('poly_name')->nullable(); - $table->string('poly_label')->nullable(); $table->date('plantstart')->nullable(); $table->date('plantend')->nullable(); $table->string('practice')->nullable(); $table->string('target_sys')->nullable(); $table->string('distr')->nullable(); $table->integer('num_trees')->nullable(); - $table->float('est_area')->nullable(); - $table->date('date_modified')->nullable(); - $table->string('country')->nullable(); + $table->decimal('calc_area', 15, 2)->nullable(); $table->string('status')->nullable(); $table->string('created_by')->nullable(); $table->string('last_modified_by')->nullable(); diff --git a/resources/docs/swagger-v2.yml b/resources/docs/swagger-v2.yml index 74a86ac98..b0f86adda 100644 --- a/resources/docs/swagger-v2.yml +++ b/resources/docs/swagger-v2.yml @@ -43997,6 +43997,51 @@ definitions: properties: email_address: type: string + SitePolygonCreateResponse: + type: object + properties: + message: + type: string + example: Site polygon created successfully + uuid: + type: string + description: UUID of the created site polygon + area: + type: number + format: double + description: Calculated area in hectares + DashboardPolygonResponse: + type: object + properties: + uuid: + type: string + SitePolygonResponse: + type: object + properties: + poly_name: + type: string + plantstart: + type: string + format: date + plantend: + type: string + format: date + practice: + type: string + target_sys: + type: string + distr: + type: string + num_trees: + type: integer + calc_area: + type: number + format: float + GeometryString: + type: object + properties: + geometry: + type: string paths: '/v2/tree-species/{entity}/{UUID}': get: @@ -93560,3 +93605,217 @@ paths: description: OK schema: type: file + '/v2/terrafund/site-polygon/{uuid}/{siteUuid}': + post: + operationId: post-v2-site-polygon-siteUuid + summary: Create site polygon + parameters: + - in: path + name: uuid + required: true + type: string + description: The UUID of the polygon related + - in: path + name: siteUuid + required: true + type: string + description: The UUID of the site + - in: body + name: body + required: true + schema: + $ref: '#/definitions/SitePolygonResponse' + responses: + '201': + description: Successful response + content: + application/json: + schema: + $ref: '#/definitions/SitePolygonCreateResponse' + '400': + description: Bad request + '500': + description: Internal server error + '/v2/terrafund/site-polygon/{uuid}': + put: + operationId: put-v2-site-polygon-uuid + summary: Update site polygon + parameters: + - in: path + name: uuid + required: true + type: string + description: The UUID of the site polygon + - in: body + name: body + required: true + schema: + $ref: '#/definitions/SitePolygonResponse' + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/definitions/SitePolygonResponse' + '400': + description: Bad request + '500': + description: Internal server error + post: + operationId: post-v2-site-polygon-uuid + summary: create site polygon for polygon + parameters: + - in: path + name: uuid + required: true + type: string + description: The uuid of the polygon + - in: body + name: body + required: true + schema: + $ref: '#/definitions/SitePolygonResponse' + responses: + '200': + description: site polygon successfully created + content: + application/json: + schema: + $ref: '#/definitions/SitePolygonCreateResponse' + '404': + description: No polygon geometry found for the given UUID + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: No polygon geometry found for the given UUID. + '500': + description: An error occurred + content: + application/json: + schema: + type: object + properties: + error: + type: string + example: Internal Server Error + '/v2/terrafund/polygon': + post: + summary: Create a new polygon + operationId: post-v2-polygon + tags: + - V2 Terrafund + requestBody: + content: + application/json: + schema: + $ref: '#/definitions/GeometryString' + responses: + '200': + description: Created + content: + application/json: + schema: + $ref: '#/definitions/DashboardPolygonResponse' + '/v2/terrafund/upload-geojson': + post: + summary: Upload GeoJSON File + description: Uploads a GeoJSON file, converts it to GeoJSON, and inserts it into the database. + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + description: The GeoJSON file to upload + uuid: + type: string + description: The UUID of the site associated with the GeoJSON file + responses: + '200': + description: GeoJSON file processed and inserted successfully + schema: + type: object + properties: + message: + type: string + uuid: + type: string + '400': + description: Bad request + '500': + description: Internal server error + '/v2/terrafund/upload-shapefile': + post: + summary: Upload shapfile File + description: Uploads a shapefile file, converts it to GeoJSON, and inserts it into the database. + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + description: The shapefile file to upload + uuid: + type: string + description: The UUID of the site associated with the shapefile file + responses: + '200': + description: Shapefile processed and inserted successfully + schema: + type: object + properties: + message: + type: string + uuid: + type: string + '400': + description: Bad request + '500': + description: Internal server error + '/v2/terrafund/upload-kml': + post: + summary: Upload KML File + description: Uploads a KML file, converts it to GeoJSON, and inserts it into the database. + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + description: The KML file to upload + uuid: + type: string + description: The UUID of the site associated with the KML file + responses: + '200': + description: KML file processed and inserted successfully + content: + application/json: + schema: + type: object + properties: + message: + type: string + uuid: + type: string + '400': + description: Bad request + '500': + description: Internal server error \ No newline at end of file diff --git a/routes/api_v2.php b/routes/api_v2.php index 426143a3e..e8246db82 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -626,6 +626,7 @@ Route::put('/site-polygon/{uuid}', [TerrafundEditGeometryController::class, 'updateSitePolygon']); Route::post('/site-polygon/{uuid}', [TerrafundEditGeometryController::class, 'createSitePolygon']); + Route::post('/site-polygon/{uuid}/{siteUuid}', [TerrafundEditGeometryController::class, 'createSiteForPolygon']); }); Route::get('/funding-programme', [FundingProgrammeController::class, 'index'])->middleware('i18n');