This commit is contained in:
Martin Folkerts 2024-07-17 14:53:46 +02:00
parent 0b1826dccb
commit 5645ad83e5
16 changed files with 358 additions and 19 deletions

View file

@ -15,7 +15,7 @@ public function index()
public function show(string $projectName,?string $currentTab = null)
{
if($project = Project::firstWhere([['name',$projectName]])) {
$availableTabs = ['downloads', 'mosaics', 'reports', 'mailings', 'settings'];
$availableTabs = ['downloads', 'mosaics', 'reports', 'mailings', 'settings', 'exports'];
return in_array($currentTab, $availableTabs) ? view('projects.show', compact(['project', 'currentTab'])) : redirect(route('project.show', [$projectName,'downloads']));
}
return abort(404);

View file

@ -16,9 +16,8 @@
class ProjectInterpolateGrowthModelJob implements ShouldQueue
{
use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected Project $project;
protected Carbon $date;
protected int $offset;
public function __construct(Project $project)
{

View file

@ -0,0 +1,32 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class newDownloadsUploaded implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private Project $project;
/**
* Create a new job instance.
*/
public function __construct(Project $project)
{
$this->project = $project;
}
/**
* Execute the job.
*/
public function handle(): void
{
$this->project->newDownloadsUploaded();
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace App\Livewire\Projects\Tabs;
use App\Models\Project;
use Livewire\Component;
class Exports extends Component
{
public Project $project;
public $formData = [];
public $showDownloadTifsModal = false;
public $showDownloadMosaicsModal = false;
public function mount()
{
$this->formData = [
'start_date' => now()->subDays(6)->format('Y-m-d'),
'end_date' => now()->subDay()->format('Y-m-d'),
];
}
public function render()
{
return view('livewire.projects.tabs.exports');
}
public function downloadRds()
{
return $this->project->getRdsAsDownload();
}
public function downloadTifs()
{
$startDate = \Carbon\Carbon::parse($this->formData['start_date']);
$endDate = \Carbon\Carbon::parse($this->formData['end_date']);
$this->showDownloadTifsModal = false;
return $this->project->getTifsAsZip(
$startDate, $endDate
);
}
public function downloadMosaics()
{
$startDate = \Carbon\Carbon::parse($this->formData['start_date']);
$endDate = \Carbon\Carbon::parse($this->formData['end_date']);
$this->showDownloadMosaicsModal = false;
return $this->project->getMosaicsAsZip(
$startDate, $endDate
);
}
}

View file

@ -2,6 +2,7 @@
namespace App\Models;
use App\Jobs\ProjectDownloadRDSJob;
use App\Jobs\ProjectDownloadTiffJob;
use App\Jobs\ProjectInterpolateGrowthModelJob;
use App\Jobs\ProjectMosiacGeneratorJob;
@ -402,4 +403,101 @@ private function getMinimumDateFromHarvestExcelFile(): Carbon
return min($carry, Carbon::instance(SharedDate::excelToDateTimeObject($value[$season_start_index])));
}, now());
}
public function newDownloadsUploaded()
{
// $this->createDownloadRecordsInDatabaseAndUpdateStatusForAllDownloadedImages();
$date = Carbon::parse('2023-01-01');
$now = Carbon::now();
$offset = (int) $date->diffInDays($now);
// dispatch_sync(new ProjectDownloadRDSJob($this, Carbon::yesterday(), 1));
dispatch_sync(new ProjectInterpolateGrowthModelJob($this));
}
public function getRdsAsDownload() {
$path = $this->download_path.'/Data/extracted_ci/cumulative_vals/combined_CI_data.rds';
return Storage::download(
$path, 'combined_CI_data.rds'
);
}
public function getTifsAsZip(Carbon $startDate, Carbon $endDate) {
$path = $this->download_path.'/merged_final_tif';
$files = collect(Storage::files($path))
->filter(fn($file) => Str::endsWith($file, '.tif'))
->filter(function ($file) use ($startDate, $endDate) {
$dateString = str_replace('.tif', '', basename($file));
$date = Carbon::parse($dateString);
return $date->between($startDate, $endDate);
});
return $this->createZipArchiveAndReturn($files);
}
public function getMosaicsAsZip(Carbon $startDate, Carbon $endDate) {
$path = $this->download_path.'/weekly_mosaic';
// create a collection of all week numbers and years for given date range
$allowedFiles = collect(CarbonPeriod::create($startDate, $endDate)->toArray())
->map(fn($date) => sprintf('week_%s_%s.tif',$date->weekOfYear, $date->year))
->unique();
$files = collect(Storage::files($path))
->filter(fn($file) => Str::endsWith($file, '.tif'))
->filter(function ($file) use ($allowedFiles) {
return $allowedFiles->contains(basename($file));
});
putenv('TMPDIR='.storage_path('app'));
return $this->createZipArchiveAndReturn($files);
}
private function createZipArchiveAndReturn(Collection $files)
{
$zipPath = storage_path('app/'.$this->download_path.'/download.zip');
$zip = new \ZipArchive();
$zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$files->each(function ($file) use ($zip) {
$zip->addFile(storage_path('app/'.$file), basename($file));
});
$zip->close();
return response()->download(
$zipPath, 'download.zip'
);
}
public function createDownloadRecordsInDatabaseAndUpdateStatusForAllDownloadedImages(): void
{
$merged_tiffs = $this->getMergedTiffList();
$this->downloads->each(function ($download) use ($merged_tiffs) {
if ($merged_tiffs->contains($download->path) && $download->status !== 'success') {
$download->setStatusSuccess();
}
});
$merged_tiffs->each(function ($path) {
if ($this->downloads()->where('path', $path)->count() === 0) {
$this->downloads()->create([
'path' => $path,
'status' => 'success',
'name' => explode('/', $path)[count(explode('/', $path)) - 1]
]);
}
});
}
public function createAllMosaicsInDatabaseAndUpdateStatusForAllDownloadedImages()
{
$list_of_desired_mosaics = $this->getMergedTiffList()
->map(fn($file) => str_replace('.tif', '', basename($file) ))
->map(function($date) {
$carbon = Carbon::parse($date);
return sprintf('week_%s_%s', $carbon->format('W'), $carbon->year);
})
->unique();
}
}

Binary file not shown.

View file

@ -0,0 +1,47 @@
@props([
'formData',
])
<x-modal wire:model.live="showDownloadMosaicsModal" {{ $attributes }} >
<x-form-modal submit="downloadMosaics" wire:loading.class="opacity-50">
<x-slot name="title">
{{ __('Download Mosaics') }}
</x-slot>
<x-slot name="description">
</x-slot>
<x-slot name="form">
<div class="col-span-6 sm:col-span-4">
<x-label for="start_date" value="{{ __('Start Date') }}"/>
<x-input id="start_date" type="date" max="{{\Carbon\Carbon::yesterday()->toDateString()}}" class="mt-1 block w-full" wire:model.live="formData.start_date" autofocus/>
<x-input-error for="formData.start_date" class="mt-2"/>
</div><div class="col-span-6 sm:col-span-4">
<x-label for="end_date" value="{{ __('End Date') }}"/>
<x-input id="end_date" type="date" max="{{\Carbon\Carbon::yesterday()->toDateString()}}" class="mt-1 block w-full" wire:model.live="formData.end_date" />
<x-input-error for="formData.end_date" class="mt-2"/>
</div>
<div>
<span class="whitespace-nowrap"></span>
</div>
</x-slot>
<x-slot name="actions">
<x-action-message class="mr-3" on="saved">
{{ __('Saved.') }}
</x-action-message>
<x-secondary-button class="mr-3"
type="button"
x-on:click="$wire.showDownloadMosaicsModal = false"
>
{{ __('Cancel') }}
</x-secondary-button>
<x-button wire:loading.disabled>
{{ __('Download') }}
</x-button>
</x-slot>
</x-form-modal>
</x-modal>

View file

@ -0,0 +1,47 @@
@props([
'formData',
])
<x-modal wire:model.live="showDownloadTifsModal" {{ $attributes }} >
<x-form-modal submit="downloadTifs" wire:loading.class="opacity-50">
<x-slot name="title">
{{ __('Download Zip with satelite data') }}
</x-slot>
<x-slot name="description">
</x-slot>
<x-slot name="form">
<div class="col-span-6 sm:col-span-4">
<x-label for="start_date" value="{{ __('Start Date') }}"/>
<x-input id="start_date" type="date" max="{{\Carbon\Carbon::yesterday()->toDateString()}}" class="mt-1 block w-full" wire:model.live="formData.start_date" autofocus/>
<x-input-error for="formData.start_date" class="mt-2"/>
</div><div class="col-span-6 sm:col-span-4">
<x-label for="end_date" value="{{ __('End Date') }}"/>
<x-input id="end_date" type="date" max="{{\Carbon\Carbon::yesterday()->toDateString()}}" class="mt-1 block w-full" wire:model.live="formData.end_date" />
<x-input-error for="formData.end_date" class="mt-2"/>
</div>
<div>
<span class="whitespace-nowrap"></span>
</div>
</x-slot>
<x-slot name="actions">
<x-action-message class="mr-3" on="saved">
{{ __('Saved.') }}
</x-action-message>
<x-secondary-button class="mr-3"
type="button"
x-on:click="$wire.showDownloadTifsModal = false"
>
{{ __('Cancel') }}
</x-secondary-button>
<x-button wire:loading.disabled>
{{ __('Save') }}
</x-button>
</x-slot>
</x-form-modal>
</x-modal>

View file

@ -4,4 +4,5 @@
<a class=" capitalize font-semibold text-md hover:rounded-lg hover:bg-gray-50 hover:text-indigo-600 hover:border-0 py-2 pl-2 pr-3 leading-6 whitespace-nowrap gap-2 flex items-baseline group text-gray-700 {{!($activeTab == 'reports') ?: 'rounded-lg bg-gray-50 text-indigo-600 border-0'}}" href="{{route('project.show',[$projectName,'reports'])}}"><i class="group-hover:text-indigo-600 w-6 text-gray-400 fg-map-stat fg-lg"></i>Reports</a>
<a class=" capitalize font-semibold text-md hover:rounded-lg hover:bg-gray-50 hover:text-indigo-600 hover:border-0 py-2 pl-2 pr-3 leading-6 whitespace-nowrap gap-2 flex items-baseline group text-gray-700 {{!($activeTab == 'mailings') ?: 'rounded-lg bg-gray-50 text-indigo-600 border-0'}}" href="{{route('project.show',[$projectName,'mailings'])}}"><i class="group-hover:text-indigo-600 w-6 text-gray-400 fa-regular fa-envelope"></i>Mailings</a>
<a class=" capitalize font-semibold text-md hover:rounded-lg hover:bg-gray-50 hover:text-indigo-600 hover:border-0 py-2 pl-2 pr-3 leading-6 whitespace-nowrap gap-2 flex items-baseline group text-gray-700 {{!($activeTab == 'settings') ?: 'rounded-lg bg-gray-50 text-indigo-600 border-0'}}" href="{{route('project.show',[$projectName,'settings'])}}"><i class="group-hover:text-indigo-600 w-6 text-gray-400 fa-solid fa-gear"></i>Settings</a>
<a class=" capitalize font-semibold text-md hover:rounded-lg hover:bg-gray-50 hover:text-indigo-600 hover:border-0 py-2 pl-2 pr-3 leading-6 whitespace-nowrap gap-2 flex items-baseline group text-gray-700 {{!($activeTab == 'exports') ?: 'rounded-lg bg-gray-50 text-indigo-600 border-0'}}" href="{{route('project.show',[$projectName,'exports'])}}"><i class="group-hover:text-indigo-600 w-6 text-gray-400 fa-solid fa-download"></i>Exports</a>
</div>

View file

@ -0,0 +1,56 @@
<div class="flex flex-col divide-y space-y-4 m-2">
<div id="edit" class="flex flex-col md:flex-row gap-2">
<div class="flex flex-col md:w-1/3">
<div class="text-xl font-bold ">
{{ __('RDS File') }}
</div>
<div class="text-gray-500 text-md">
{{__('Download the RDS file for this project')}}
</div>
</div>
<div class="md:w-2/3">
<button wire:click="downloadRds" class="inline-flex items-center rounded-md bg-indigo-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" >Download RDS</button>
</div>
</div>
<div
class="flex flex-col md:flex-row">
<div class="flex flex-col my-4 md:w-1/3">
<span class="text-xl font-bold dark:text-gray-300">{{__('Download Tifs')}}</span>
<div class="text-gray-500 text-md">
{{ __('Create a zip file with satelite images for a given time series') }}
</div>
</div>
<div id="mail" class="flex flex-col md:w-2/3" > {{-- flex col--}}
<div class="flex justify-between my-4">
<div class="inline-flex items-center gap-2">
<div class=" text-md font-bold">
<button x-on:click="$wire.showDownloadTifsModal = true" class="inline-flex items-center rounded-md bg-indigo-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" >Download tifs</button>
</div>
</div>
</div>
<div class="flex w-full "></div>
</div>
</div>
<div
class="flex flex-col md:flex-row">
<div class="flex flex-col my-4 md:w-1/3">
<span class="text-xl font-bold dark:text-gray-300">{{__('Download Mosaics')}}</span>
<div class="text-gray-500 text-md">
{{ __('Create a zip file with mosaic images for a given time series') }}
</div>
</div>
<div id="mail" class="flex flex-col md:w-2/3" > {{-- flex col--}}
<div class="flex justify-between my-4">
<div class="inline-flex items-center gap-2">
<div class=" text-md font-bold">
<button x-on:click="$wire.showDownloadMosaicsModal = true" class="inline-flex items-center rounded-md bg-indigo-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" >Download Mosaics</button>
</div>
</div>
</div>
<div class="flex w-full "></div>
</div>
</div>
<div class="flex justify-end items-center py-4"></div>
<x-download-mosaics-as-zip-modal :formData="$formData"></x-download-mosaics-as-zip-modal>
<x-download-tifs-as-zip-modal :formData="$formData"></x-download-tifs-as-zip-modal>
</div>

View file

@ -24,6 +24,9 @@
@case('settings')
<livewire:projects.tabs.settings :project="$project"></livewire:projects.tabs.settings>
@break
@case('exports')
<livewire:projects.tabs.exports :project="$project"></livewire:projects.tabs.exports>
@break
@default
<div class="flex w-full h-screen justify-center items-center"> Menu Component not found.</div>
@endswitch

View file

@ -329,4 +329,5 @@ public static function scheduleDayProvider()
];
}
}

View file

@ -230,20 +230,20 @@ create_CI_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend =
tm_shape(pivot_raster, unit = "m")+
tm_raster(breaks = c(0,0.5,1,2,3,4,5,6,7,Inf), palette = "RdYlGn",legend.is.portrait = legend_is_portrait ,midpoint = NA) +
tm_layout(main.title = paste0("\nMax CI week ", week,"\n", age, " weeks old"),
main.title.size = 0.7, legend.show = show_legend) +
tm_shape(pivot_shape) +
tm_borders(lwd = 3) + tm_text("sub_field", size = 1/2) +
tm_shape(pivot_spans) + tm_borders(lwd = 0.5, alpha=0.5)
main.title.size = 0.7, legend.show = show_legend) # +
# tm_shape(pivot_shape) +
# tm_borders(lwd = 3) + tm_text("sub_field", size = 1/2) +
# tm_shape(pivot_spans) + tm_borders(lwd = 0.5, alpha=0.5)
}
create_CI_diff_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend = F, legend_is_portrait = F, week_1, week_2, age){
tm_shape(pivot_raster, unit = "m")+
tm_raster(breaks = c(-3,-2,-1,0,1,2, 3), palette = "RdYlGn",legend.is.portrait = legend_is_portrait ,midpoint = 0, title = "CI difference") +
tm_layout(main.title = paste0("CI change week ", week_1, "- week ",week_2, "\n", age," weeks old"),
main.title.size = 0.7, legend.show = show_legend) +
tm_shape(pivot_shape) +
tm_borders(lwd = 3) + tm_text("sub_field", size = 1/2) +
tm_shape(pivot_spans) + tm_borders(lwd = 0.5, alpha=0.5)
main.title.size = 0.7, legend.show = show_legend) # +
# tm_shape(pivot_shape) +
# tm_borders(lwd = 3) + tm_text("sub_field", size = 1/2) +
# tm_shape(pivot_spans) + tm_borders(lwd = 0.5, alpha=0.5)
}
ci_plot <- function(pivotName){

Binary file not shown.

View file

@ -27,8 +27,6 @@ planet_tif_folder <- here(laravel_storage_dir, "merged_tif")
merged_final <- here(laravel_storage_dir, "merged_final_tif")
new_project_question = FALSE
planet_tif_folder <- here(laravel_storage_dir, "merged_tif")
merged_final <- here(laravel_storage_dir, "merged_final_tif")
data_dir <- here(laravel_storage_dir, "Data")
extracted_CI_dir <- here(data_dir, "extracted_ci")
@ -53,7 +51,7 @@ pivot_stats2 <- readRDS(here(cumulative_CI_vals_dir,"combined_CI_data.rds")) %>%
# gather data into long format for easier calculation and visualisation
pivot_stats_long <- pivot_stats2 %>%
tidyr::gather("Date", value, -field, -sub_field ) %>%
mutate(Date = right(Date, 8),
mutate(#Date = right(Date, 8),
Date = lubridate::ymd(Date)
) %>%
drop_na(c("value","Date")) %>%
@ -72,7 +70,7 @@ pivot_stats_long <- pivot_stats2 %>%
# pivot_select_model_Data_2023 <- harvesting_data %>% filter(year == 2023) %>% filter(!is.na(season_start)) %>% pull(sub_field)
pivot_select_model_Data_2024 <- harvesting_data %>% filter(year == 2024)%>% filter(!is.na(season_start)) %>% pull(sub_field)
print(pivot_select_model_Data_2024)
message(pivot_select_model_Data_2024)
# pivots_dates_Data_2022 <- pivots_dates0 %>% filter(!is.na(season_end_2022))
# pivot_select_model_Data_2022 <- unique(pivots_dates_Data_2022$pivot_quadrant )
#
@ -83,14 +81,14 @@ print(pivot_select_model_Data_2024)
# pivot_select_model_Data_2024 <- unique(pivots_dates_Data_2024$pivot_quadrant)
extract_CI_data <- function(field_names, harvesting_data, field_CI_data, season) {
# field_names = "4042902"
# field_names = "Nandi A1a"
# field_names = "1.13A"
# harvesting_data = harvesting_data
# field_CI_data = pivot_stats_long
# season= 2024
filtered_harvesting_data <- harvesting_data %>%
na.omit() %>%
# na.omit() %>%
filter(year == season, sub_field %in% field_names)
filtered_field_CI_data <- field_CI_data %>%
@ -125,6 +123,7 @@ extract_CI_data <- function(field_names, harvesting_data, field_CI_data, season)
# message('2023')
Data_2024 <- map(pivot_select_model_Data_2024, ~ extract_CI_data(.x, harvesting_data = harvesting_data, field_CI_data = pivot_stats_long, season = 2024)) %>% list_rbind()
message('2024')
head(Data_2024)
#CI_all <- rbind(Data_2023, Data_2024)

View file

@ -92,7 +92,7 @@ if(project_dir == "chemba"){
names(field_boundaries_sf) <- c("field", "sub_field", "geometry")
field_boundaries <- field_boundaries_sf %>% vect()
harvesting_data <- read_excel(here(data_dir, "harvest.xlsx"),
col_types = c("numeric", "numeric", "numeric",
col_types = c("text", "text", "numeric",
"date", "date", "numeric", "text",
"numeric", "numeric")) %>%
mutate(season_end = case_when(