Vai al contenuto

Angolo interno minimo di un poligono↵

Come trovare l'angolo interno più piccolo (più stretto) di un poligono utilizzando il calcolatore di campi.

Problema↵

Dato un poligono, si vuole calcolare quale sia l'angolo interno più piccolo formato dai suoi vertici. Questo può essere utile per:

  • Identificare poligoni con angoli molto stretti
  • Validare geometrie
  • Analizzare la forma degli edifici
  • Controllo qualità dei dati geometrici

Soluzione con espressione↵

Espressione completa↵

array_min(
    array_foreach(
        generate_series(1, num_points(@geometry)-1),
        with_variable('curr', point_n(@geometry, @element),
            with_variable('prev', point_n(@geometry, if(@element=1, num_points(@geometry)-1, @element-1)),
                with_variable('next', point_n(@geometry, if(@element=num_points(@geometry)-1, 1, @element+1)),
                    with_variable('diff', 
                        abs(
                            degrees(azimuth(@curr, @prev)) - 
                            degrees(azimuth(@curr, @next))
                        ),
                        -- Calcola l'angolo più stretto (tra 0 e 180)
                        min(@diff, 360 - @diff)
                    )
                )
            )
        )
    )
)

Output: Il valore dell'angolo interno minimo espresso in gradi (0-180°)

Spiegazione dettagliata↵

1. Generazione della sequenza di vertici↵

generate_series(1, num_points($geometry)-1)

Cosa fa:

  • num_points($geometry) → Conta il numero totale di vertici del poligono
  • generate_series(1, n-1) → Crea una sequenza da 1 a n-1 (esclude l'ultimo vertice perché nei poligoni il primo e l'ultimo coincidono)

Esempio pratico:

Per un poligono con 6 vertici, genera: [1, 2, 3, 4, 5]

2. Iterazione sui vertici con array_foreach↵

array_foreach(
    generate_series(...),
    ...
)

Cosa fa:

  • Itera su ogni numero della sequenza
  • Per ogni iterazione, @element contiene il numero del vertice corrente
  • Esegue il calcolo dell'angolo per ciascun vertice

3. Identificazione dei tre punti consecutivi↵

with_variable('curr', point_n($geometry, @element),
  with_variable('prev', point_n($geometry, if(@element=1, num_points($geometry)-1, @element-1)),
    with_variable('next', point_n($geometry, if(@element=num_points($geometry)-1, 1, @element+1)),
      ...
    )
  )
)

Cosa fa:

  • @curr → Vertice corrente (quello per cui calcoliamo l'angolo)
  • @prev → Vertice precedente (gestisce il caso del primo vertice)
  • @next → Vertice successivo (gestisce il caso dell'ultimo vertice)

Gestione casi speciali:

  • Se @element = 1 (primo vertice): @prev diventa l'ultimo vertice reale
  • Se @element = num_points-1 (ultimo vertice): @next diventa il primo vertice

4. Calcolo dell'angolo↵

with_variable('diff', 
  abs(
    degrees(azimuth(@curr, @prev)) - 
    degrees(azimuth(@curr, @next))
  ),
  min(@diff, 360 - @diff)
)

Cosa fa (passo per passo):

  1. azimuth(@curr, @prev) → Calcola l'azimut (direzione) dal vertice corrente a quello precedente (in radianti)
  2. azimuth(@curr, @next) → Calcola l'azimut dal vertice corrente a quello successivo
  3. degrees(...) → Converte da radianti a gradi
  4. Sottrazione → Calcola la differenza tra i due angoli
  5. abs(...) → Prende il valore assoluto (angolo sempre positivo)
  6. min(@diff, 360 - @diff) → Normalizza l'angolo tra 0° e 180° (sceglie l'angolo più piccolo tra i due possibili)

Esempio numerico:

Se l'azimut verso @prev è 45° e verso @next è 135°: - Differenza: |45° - 135°| = 90° - min(90°, 360° - 90°) = min(90°, 270°) = 90°

Se la differenza fosse 270°: - min(270°, 360° - 270°) = min(270°, 90°) = 90°

5. Selezione del minimo↵

array_min(
  array_foreach(...)
)

Cosa fa:

  • array_foreach crea un array con tutti gli angoli calcolati per ogni vertice
  • array_min seleziona il valore minimo dall'array

Esempio:

Se gli angoli sono [120°, 95°, 75°, 110°, 85°], restituisce 75°

Come utilizzare l'espressione↵

Passaggi:↵

  1. Aprire la tabella degli attributi del layer poligonale
  2. Aprire il Calcolatore di Campi (icona dell'abaco o Ctrl+I)
  3. Configurare il nuovo campo:
  4. Crea un nuovo campo
  5. Nome campo: angolo_min (o il nome che si preferisce)
  6. Tipo di campo: Numero decimale (double o real)
  7. Lunghezza: 10
  8. Precisione: 2
  9. Incollare l'espressione nell'editor
  10. Cliccare OK per calcolare il campo

Applicazioni pratiche↵

1. Selezione poligoni con angoli stretti↵

Selezionare tutti i poligoni con angoli interni minori di 30°:

"angolo_min" < 30

2. Validazione geometrie↵

Identificare edifici con angoli troppo acuti (possibili errori di digitalizzazione):

"angolo_min" < 15

3. Tematizzazione↵

Colorare i poligoni in base all'angolo minimo:

  • Rosso: angoli < 30° (geometrie problematiche)
  • Giallo: angoli tra 30° e 60°
  • Verde: angoli > 60° (geometrie regolari)

Varianti dell'espressione↵

Trovare l'angolo massimo invece del minimo↵

Sostituire array_min con array_max:

array_max(
  array_foreach(...)
)

Calcolare la media degli angoli interni↵

Sostituire array_min con array_mean:

array_mean(
  array_foreach(...)
)

Contare quanti angoli sono sotto una soglia↵

array_length(
  array_filter(
    array_foreach(...),
    @element < 30
  )
)

Note importanti↵

Tipo di geometria↵

L'espressione funziona con:

  • ✅ Poligoni singoli (Polygon)
  • ⚠️ Multi-poligoni (solo il primo anello del primo poligono)
  • ❌ Linee e punti (non applicabile)

Anelli interni (buchi)↵

L'espressione calcola solo l'anello esterno del poligono. Per analizzare anche i buchi interni, serve un'espressione più complessa.

Performance↵

L'espressione è relativamente complessa. Su dataset molto grandi (migliaia di poligoni con molti vertici):

  • Considerare di salvare il risultato in un campo fisico
  • Valutare l'uso di Processing Python per performance migliori

Unità di misura↵

Il risultato è sempre in gradi (0-180°), indipendentemente dal sistema di coordinate del layer.

Risoluzione problemi↵

Risultato NULL↵

Causa: Il poligono ha meno di 3 vertici (geometria invalida)

Soluzione: Verificare e correggere le geometrie con "Controlla validità"

Risultato 0↵

Causa:

Tre vertici consecutivi sono perfettamente allineati, formando un angolo di 180° (angolo piatto). Questo significa che:

  • Il vertice intermedio giace esattamente sulla linea tra i due vertici adiacenti
  • Non c'è realmente un "angolo" in quel punto
  • La geometria potrebbe avere un vertice ridondante

Esempi tipici:

  • Poligoni con vertici non necessari lungo un lato retto
  • Errori di digitalizzazione
  • Geometrie semplificate male

Soluzione:

  1. Rimuovere vertici ridondanti: Strumenti → Geometria → Semplifica geometria
  2. Verificare manualmente: Usare lo strumento di modifica vertici per controllare
  3. Se intenzionale: Il valore 0° è corretto per quella geometria

Risultato 180↵

Causa:

Angolo molto ampio (quasi piatto ma dall'altra parte)

Soluzione:

Verificare la geometria, potrebbe essere corretta o indicare un errore di digitalizzazione

Errore "point_n not found"↵

Causa: Versione di QGIS troppo vecchia

Soluzione: Aggiornare QGIS alla versione 3.0 o superiore

Soluzioni alternative↵

Virtual Layer (SQL con Spatialite)↵

Non esiste una funzione SQL standard equivalente. È necessario usare l'espressione QGIS o uno script Python.

Processing Python↵

Per analisi più complesse o dataset molto grandi, considerare uno script Python che usa la libreria Shapely:

from shapely.geometry import Point
import math

def calculate_min_angle(geom):
    coords = list(geom.exterior.coords)[:-1]  # Escludi ultimo punto
    angles = []

    for i in range(len(coords)):
        prev = coords[i-1]
        curr = coords[i]
        next_pt = coords[(i+1) % len(coords)]

        # Calcola angoli
        angle1 = math.atan2(prev[1]-curr[1], prev[0]-curr[0])
        angle2 = math.atan2(next_pt[1]-curr[1], next_pt[0]-curr[0])

        diff = abs(math.degrees(angle1 - angle2))
        angle = min(diff, 360 - diff)
        angles.append(angle)

    return min(angles)

Funzioni e variabili utilizzate: