2 visitas

Cómo importar archivos Shapefile en PostgreSQL con PostGIS usando Laravel

Aprendé cómo importar archivos shapefile a una base de datos PostgreSQL con PostGIS usando Laravel. Te mostramos cómo validar los archivos, generar registros relacionados y cargar geometrías usando un servicio Laravel reutilizable, sin depender de formularios ni interfaces.

En este artículo te muestro cómo podés subir archivos Shapefile (SHP) y guardarlos en una base de datos PostgreSQL con soporte PostGIS, utilizando Laravel 12 y PHP 8.4.


🧩 Requisitos

  • PostgreSQL con la extensión postgis habilitada.
  • Laravel 12.
  • Comando shp2pgsql disponible en el servidor (parte del paquete postgis).
  • Una tabla que tenga una columna geometry y un campo para identificar el archivo que subió los datos (ej: file_id).

🗂 Archivos que componen un shapefile

Un shapefile necesita al menos tres archivos para poder procesarse:

  • .shp (geometría)
  • .shx (índices)
  • .dbf (atributos)

El sistema debe verificar que estén todos presentes antes de importar.


💻 Servicio Laravel para importar shapefiles

A continuación, te dejo un servicio Laravel que:

  1. Valida que estén los archivos necesarios.
  2. Crea un registro para identificar la carga del archivo.
  3. Usa shp2pgsql para convertir el shapefile en SQL.
  4. Inserta los datos en una tabla con geometría.
  5. Relaciona cada geometría con su archivo usando un file_id.
namespace App\Services;

use App\Models\File;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;

class ShapeImportService
{
    private array $requiredExtensions = ['shp', 'shx', 'dbf'];

    /**
     * Importa un shapefile completo y lo guarda en la base de datos.
     */
    public function importShape(array $files, int|string $userId): File
    {
        $this->validateFiles($files);

        $filename = $this->getShpFilename($files);

        // Registrar el archivo en la base
        $fileModel = File::create([
            'user_id' => $userId,
            'filename' => $filename,
        ]);

        $fileId = $fileModel->id;
        $dir = storage_path("app/shapes/{$fileId}");
        mkdir($dir, 0777, true);

        // Guardar todos los archivos en disco
        foreach ($files as $file) {
            $file->move($dir, $file->getClientOriginalName());
        }

        $shpPath = $dir . '/' . $filename;
        $sqlPath = $dir . '/import.sql';
        $tempTable = 'temp_shape_' . Str::uuid()->toString();

        // Ejecutar shp2pgsql para convertir a SQL con geometría
        $command = [
            'shp2pgsql',
            '-s', '4326',
            '-g', 'geom',
            $shpPath,
            $tempTable,
        ];

        $process = new Process($command);
        $process->run();

        if (!$process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        file_put_contents($sqlPath, $process->getOutput());

        DB::transaction(function () use ($sqlPath, $tempTable, $fileId) {
            // Crear tabla temporal
            DB::unprepared(file_get_contents($sqlPath));

            // Insertar en la tabla final mapeando los campos y la geometría
            DB::insert("
                INSERT INTO your_table_name (
                    file_id,
                    -- otros campos...
                    geom,
                    created_at,
                    updated_at
                )
                SELECT 
                    ?,
                    -- otros campos...
                    geom,
                    now(),
                    now()
                FROM {$tempTable}
            ", [$fileId]);

            // Eliminar tabla temporal
            DB::unprepared("DROP TABLE IF EXISTS {$tempTable}");
        });

        return $fileModel;
    }

    /**
     * Extrae el nombre del archivo .shp del array de archivos subidos.
     */
    private function getShpFilename(array $files): string
    {
        foreach ($files as $file) {
            if (str_ends_with($file->getClientOriginalName(), '.shp')) {
                return $file->getClientOriginalName();
            }
        }

        throw new \RuntimeException('No se encontró archivo .shp');
    }

    /**
     * Verifica que estén presentes los archivos .shp, .shx y .dbf
     */
    private function validateFiles(array $files): void
    {
        $found = collect($files)->map(fn(UploadedFile $f) => strtolower($f->getClientOriginalExtension()));

        $missing = collect($this->requiredExtensions)
            ->diff($found);

        if ($missing->isNotEmpty()) {
            throw new \RuntimeException('Faltan archivos del shapefile: ' . $missing->implode(', '));
        }
    }
}
Cómo importar archivos Shapefile en PostgreSQL con PostGIS usando Laravel
19 April 2025

Me dedico a crear soluciones web eficientes y a compartir mi conocimiento con la comunidad de desarrolladores.

Alejandro Leone
Backend Developer