210 lines
7.0 KiB
PHP
210 lines
7.0 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Livewire;
|
||
|
|
|
||
|
|
use Livewire\Component;
|
||
|
|
use Livewire\WithFileUploads;
|
||
|
|
use Livewire\Attributes\Layout;
|
||
|
|
use App\Models\Project;
|
||
|
|
use App\Models\Phase;
|
||
|
|
use App\Models\Layer;
|
||
|
|
use App\Services\SpatialFileConverter;
|
||
|
|
use Illuminate\Support\Facades\Auth;
|
||
|
|
use Illuminate\Support\Facades\Storage;
|
||
|
|
|
||
|
|
#[Layout('layouts.app')]
|
||
|
|
class LayerManager extends Component
|
||
|
|
{
|
||
|
|
use WithFileUploads;
|
||
|
|
|
||
|
|
public Project $project;
|
||
|
|
public Phase $phase;
|
||
|
|
public $layers;
|
||
|
|
public $selectedLayer = null;
|
||
|
|
public $visibleLayers = []; // IDs de capas visibles
|
||
|
|
|
||
|
|
public $uploadFile = null;
|
||
|
|
public $layerName = '';
|
||
|
|
public $layerColor = '#3b82f6';
|
||
|
|
public $manualGeojson = null;
|
||
|
|
public $drawingMode = false;
|
||
|
|
|
||
|
|
protected $rules = [
|
||
|
|
'uploadFile' => 'required|file|mimes:geojson,kmz,kml,shp,dwg,zip|max:51200',
|
||
|
|
'layerName' => 'required|string|max:255',
|
||
|
|
'layerColor' => 'nullable|string|size:7',
|
||
|
|
];
|
||
|
|
|
||
|
|
public function mount(Project $project, Phase $phase)
|
||
|
|
{
|
||
|
|
$this->project = $project;
|
||
|
|
$this->phase = $phase;
|
||
|
|
$this->loadLayers();
|
||
|
|
if ($this->phase->project_id !== $this->project->id) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
// Por defecto todas visibles
|
||
|
|
$this->visibleLayers = $this->layers->pluck('id')->toArray();
|
||
|
|
$this->emitInitialLayersData();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function loadLayers()
|
||
|
|
{
|
||
|
|
$this->layers = Layer::where('phase_id', $this->phase->id)->latest()->get();
|
||
|
|
// Eliminar de visibles las que ya no existen
|
||
|
|
$this->visibleLayers = array_intersect($this->visibleLayers, $this->layers->pluck('id')->toArray());
|
||
|
|
}
|
||
|
|
|
||
|
|
private function emitInitialLayersData()
|
||
|
|
{
|
||
|
|
$layersData = $this->layers->map(function($layer) {
|
||
|
|
return [
|
||
|
|
'id' => $layer->id,
|
||
|
|
'geojson' => $layer->geojson_data,
|
||
|
|
'color' => $layer->geojson_data['style']['color'] ?? '#3b82f6',
|
||
|
|
];
|
||
|
|
});
|
||
|
|
$this->dispatch('initialLayersData', [
|
||
|
|
'layers' => $layersData,
|
||
|
|
'visibleLayers' => $this->visibleLayers,
|
||
|
|
'selectedLayerId' => $this->selectedLayer?->id,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function toggleLayerVisibility($layerId)
|
||
|
|
{
|
||
|
|
if ($this->selectedLayer && $this->selectedLayer->id == $layerId) {
|
||
|
|
session()->flash('info', 'No puedes ocultar la capa que estás editando.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (in_array($layerId, $this->visibleLayers)) {
|
||
|
|
$this->visibleLayers = array_diff($this->visibleLayers, [$layerId]);
|
||
|
|
} else {
|
||
|
|
$this->visibleLayers[] = $layerId;
|
||
|
|
}
|
||
|
|
$this->dispatch('visibilityChanged', $this->visibleLayers);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function selectLayer($layerId)
|
||
|
|
{
|
||
|
|
$this->selectedLayer = Layer::find($layerId);
|
||
|
|
if (!$this->selectedLayer) return;
|
||
|
|
// Asegurar que la capa seleccionada está visible
|
||
|
|
if (!in_array($layerId, $this->visibleLayers)) {
|
||
|
|
$this->visibleLayers[] = $layerId;
|
||
|
|
$this->dispatch('visibilityChanged', $this->visibleLayers);
|
||
|
|
}
|
||
|
|
$geojson = $this->selectedLayer->geojson_data;
|
||
|
|
$this->dispatch('layerSelectedForEdit', [
|
||
|
|
'layerId' => $layerId,
|
||
|
|
'geojson' => $geojson,
|
||
|
|
'color' => $geojson['style']['color'] ?? '#3b82f6',
|
||
|
|
]);
|
||
|
|
session()->flash('info', 'Editando capa: ' . $this->selectedLayer->name);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function importFile()
|
||
|
|
{
|
||
|
|
$this->validate();
|
||
|
|
$user = Auth::user();
|
||
|
|
if (!$user->can('upload layers') && !$user->hasRole('Admin')) {
|
||
|
|
session()->flash('error', 'Sin permisos.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$projectDir = "uploads/projects/{$this->project->id}/layers";
|
||
|
|
$originalPath = $this->uploadFile->store($projectDir, 'public');
|
||
|
|
$geojson = SpatialFileConverter::convertToGeoJson($this->uploadFile);
|
||
|
|
if (!$geojson) {
|
||
|
|
session()->flash('error', 'Conversión fallida.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
$geojson['style'] = ['color' => $this->layerColor ?: '#3b82f6'];
|
||
|
|
|
||
|
|
$layer = Layer::create([
|
||
|
|
'project_id' => $this->project->id,
|
||
|
|
'phase_id' => $this->phase->id,
|
||
|
|
'name' => $this->layerName,
|
||
|
|
'geojson_data' => $geojson,
|
||
|
|
'original_file' => $originalPath,
|
||
|
|
'uploaded_by' => $user->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->loadLayers();
|
||
|
|
$this->visibleLayers[] = $layer->id;
|
||
|
|
$this->reset(['uploadFile', 'layerName']);
|
||
|
|
$this->emitInitialLayersData();
|
||
|
|
session()->flash('message', 'Capa importada.');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function createEmptyLayer()
|
||
|
|
{
|
||
|
|
$user = Auth::user();
|
||
|
|
$emptyGeojson = [
|
||
|
|
'type' => 'FeatureCollection',
|
||
|
|
'features' => [],
|
||
|
|
'style' => ['color' => $this->layerColor ?: '#3b82f6']
|
||
|
|
];
|
||
|
|
$layer = Layer::create([
|
||
|
|
'project_id' => $this->project->id,
|
||
|
|
'phase_id' => $this->phase->id,
|
||
|
|
'name' => $this->layerName ?: 'Nueva capa',
|
||
|
|
'geojson_data' => $emptyGeojson,
|
||
|
|
'original_file' => null,
|
||
|
|
'uploaded_by' => $user->id,
|
||
|
|
]);
|
||
|
|
$this->loadLayers();
|
||
|
|
$this->visibleLayers[] = $layer->id;
|
||
|
|
$this->selectLayer($layer->id);
|
||
|
|
$this->emitInitialLayersData();
|
||
|
|
session()->flash('message', 'Capa vacía creada y seleccionada.');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function saveManualGeojson($geojsonString)
|
||
|
|
{
|
||
|
|
if (!$this->selectedLayer) {
|
||
|
|
session()->flash('error', 'No hay capa seleccionada.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
$geojson = json_decode($geojsonString, true);
|
||
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||
|
|
session()->flash('error', 'GeoJSON inválido.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
$geojson['style'] = ['color' => $this->layerColor ?: ($this->selectedLayer->geojson_data['style']['color'] ?? '#3b82f6')];
|
||
|
|
$this->selectedLayer->geojson_data = $geojson;
|
||
|
|
$this->selectedLayer->save();
|
||
|
|
|
||
|
|
$this->loadLayers(); // recargar por si acaso
|
||
|
|
$this->selectLayer($this->selectedLayer->id);
|
||
|
|
$this->emitInitialLayersData();
|
||
|
|
session()->flash('message', 'Capa guardada.');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function deleteLayer($layerId)
|
||
|
|
{
|
||
|
|
$user = Auth::user();
|
||
|
|
if (!$user->can('delete layers') && !$user->hasRole('Admin')) abort(403);
|
||
|
|
$layer = Layer::find($layerId);
|
||
|
|
if (!$layer) return;
|
||
|
|
if ($layer->original_file) Storage::disk('public')->delete($layer->original_file);
|
||
|
|
$layer->delete();
|
||
|
|
$this->loadLayers();
|
||
|
|
if ($this->selectedLayer && $this->selectedLayer->id == $layerId) {
|
||
|
|
$this->selectedLayer = null;
|
||
|
|
}
|
||
|
|
$this->emitInitialLayersData();
|
||
|
|
session()->flash('message', 'Capa eliminada.');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function cancelEditing()
|
||
|
|
{
|
||
|
|
$this->selectedLayer = null;
|
||
|
|
$this->dispatch('layerSelectedForEdit', null);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function render()
|
||
|
|
{
|
||
|
|
return view('livewire.layer-manager');
|
||
|
|
}
|
||
|
|
}
|