Funziona male l'espressione overlay_nearest - Edit: no!

Ho notato (ambito tematizzazione) che la funzione overlay_nearest [1] non restituisce i valori attesi se utilizzo parametri opzionali come limit, mi spiego meglio con un esempio:

Su un layer di punti, creo una tematizzazione personalizzata usando un layer di stile come Generatore di Geometria:

usando l’espressione:

with_variable(
  'center',
  @map_extent_center,
  collect_geometries(
    array_foreach(
      overlay_nearest(
        layer:='centroidi',
        expression:=$geometry
	,limit:=8
      ),
      make_line(@center, @element)
    )
  )
)

il mio obiettivo è generare una sorta di ‘ragno’ a 8 zampe con centro @map_extent_center (che è il centro della mappa visualizzata) e gli otto punti più vicini, sotto un esempio atteso:

Accade però che l’opzione limit:=8 non fa quello che mi aspetto, ovvero che limiti i collegamenti solo ai primi 8 punti più vicini, ma li collega tutti!!!

cosa sto sbagliando? oppure è un bug?

Sto usando OSGeo4W

saluti


[1] 9.2. List of functions — QGIS Documentation documentation

Ciao Salvatore,
non penso che il problema sia nel funzionamento della funzione overlay_nearest, ma nella logica dell’espressione applicata al generatore di geometrie.

Nel generatore di geometrie, come nel calcolatore di campi, l’espressione viene calcolata per ogni geometria presente nel layer a cui è applicata.

La funzione overlay_nearest trova le feature contenute nel layer che viene indicato nel primo parametro che siano più vicine alla “geometria corrente” del layer a cui è applicata. Mentre tu vorresti trovare le feature contenute nel layer che viene indicato nel primo parametro che siano più vicine al centro della mappa.

L’espressione, tramite la funzione overlay_nearest, trova gli 8 punti (del layer “centroidi”) che sono più vicini al primo punto del layer a cui è applicata (suppongo lo stesso layer “centroidi”) e genera le linee tra il centro della mappa e gli 8 punti più vicini al primo punto del layer; poi trova gli 8 punti che sono più vicini al secondo punto del layer e genera le linee tra il centro della mappa e gli 8 punti che sono più vicini al secondo punto del layer; e così via… Ovviamente in questo modo tutti (o quasi) i punti presenti nel layer “centroidi” verranno collegati al centro della mappa.
Quindi non è quello che immagino tu voglia ottenere.

Puoi verificare tu stesso che sia la funzione overlay_nearest sia l’espressione funzionino correttamente (ma non come avresti voluto), applicando la stessa identica espressione nel generatore dei geometrie di un altro layer che contenga una sola feature: vedrai solo 8 linee che collegano il centro della mappa agli 8 punti del layer “centroidi” che sono più vicini all’unica feature di questo altro layer (ma non a quelli più vicini al centro della mappa).

Non so se è possibile ottenere il risultato richiesto tramite un’espressione nel generatore di geometrie…

A presto.

Andrea

Ciao Andrea,

ho fatto i seguenti test:

Primo

Sullo stesso layer Centroidi applico questa espressione nel generatore di geometrie, usando un punto fisso:

with_variable(
  'center',
 make_point(1081367,4089509), -- punto fisso, unica feature
  collect_geometries(
    array_foreach(
      overlay_nearest(
        layer:='Centroidi',
        expression:=$geometry
	,limit:=8
      ),
      make_line(@center, @element)
    )
  )
)

ottengo stesso risultato di prima, il punto di collega a tutti i punti del layer Centroide.

Secondo

Creo un nuovo layer puntuale con un solo punto, l’espressione diventa

with_variable(
  'center',
 geometry(get_feature_by_id('punto',0)),
  collect_geometries(
    array_foreach(
      overlay_nearest(
        layer:='Centroidi',
        expression:=$geometry
	,limit:=8
      ),
      make_line(@center, @element)
    )
  )
)

ottengo stesso risultato, il punto è collegato con tutti gli altri.

grazie per il tempo che ci dedichi

saluti

Probabilmente non sono stato sufficientemente chiaro.

Puoi verificare tu stesso che sia la funzione overlay_nearest sia l’espressione funzionino correttamente, applicando la stessa identica espressione

with_variable(
  'center',
  @map_extent_center,
  collect_geometries(
    array_foreach(
      overlay_nearest(
        layer:='centroidi',
        expression:=$geometry
	,limit:=8
      ),
      make_line(@center, @element)
    )
  )
)

nel generatore dei geometrie di un altro layer che contenga una sola feature (il layer “punto”) : vedrai solo 8 linee che collegano il centro della mappa agli 8 punti del layer “centroidi” che sono più vicini all’unica feature del layer “punto”.

Ora ho capito quello che vuoi dire, ma non serve a nulla così fatto.
2025-05-13_10h38_57

Deve esserci una soluzione.

saluti

Certo, ma dimostra che la funzione overlay_nearest e il suo parametro limit funzionano correttamente.

L’espressione che hai creato però si basa su una idea che non tiene conto di come funziona il generatore di geometrie (e il calcolatore di campi) e quindi non restituisce il risultato voluto.
Spero sia riuscito a spiegare perché tale espressione non possa funzionare come da te sperato.

Devi farti venire un’altra idea! :slight_smile:

Per esempio, se le coordinate del punto nel layer “punto” potessero essere aggiornate automaticamente con le coordinate del centro della mappa, il problema sarebbe risolto facilmente, utilizzando la stessa identica espressione applicata sempre al layer “punto”.

Ciao Andrea,
purtroppo l’espressione non la ho creata io ma Claude AI, probabilmente ho scritto male il prompt.
Queste sono alcune conseguenze dell’uso delle AI, se usate male (come in questo caso) si rischia di fare brutte figure.

Non ho verificato concettualmente l’espressione ed ho scritto subito l’email.

Hai perfettamente ragione, l’espressione non potrà mai funzionare per come avevo pensato.

saluti

1 Like

Penso che l’unico modo di procedere, dovendo utilizzare esclusivamente un’espressione nel generatore di geometrie, sia quello di creare un’espressione che mimi il funzionamento di overlay_nearest ma potendo indicare il punto a partire dal quale calcolare la distanza di ogni punto per individuare i punti più vicini e poi creare la linea dal centro al punto se il punto è uno degli 8 punti più vicini.

Per esempio:

if(
  array_contains(
    array_slice(
      array_agg(
        @id,
        order_by := distance(@geometry, @map_extent_center)
      ), 0, 7
    ),
    @id
  ),
  make_line(@geometry, @map_extent_center),
  none
)

Che ne pensi?

Andrea

1 Like

Ottimo, funziona.

Se però aggiungo una mappa di sfondo come OSM non funziona più!

Guarda questa demo.

Grazie mille

Ho preso il layer comuni istat in 32632, estratto i centroidi e su questo applicato stile per simulare il ragno.

Il problema non dovrebbe dipendere dalla presenza del layer OSM, ma dal fatto che probabilmente il CRS del progetto è diverso dal CRS del layer al quale hai applicato l’espressione nel generatore di geometrie.

I parametri di input delle funzioni distance e make_line sono “geometrie”, cioè sono semplici coordinate, senza indicazione del CRS; quindi, per ottenere un risultato che abbia senso, bisogna normalmente verificare e fare in modo che essi siano fra loro compatibili, cioè sostanzialmente che i loro valori siano espressi nello stesso CRS.

Inoltre l’espressione del generatore di geometrie dovrebbe restituire una geometria le cui coordinate siano espresse normalmente nello stesso CRS del layer a cui è applicata.

In particolare, l’espressione, oltre alle geometrie delle feature del layer, che ovviamente hanno le coordinate espresse nel CRS del layer, usa la geometria restituita dalla variabile @map_extent_center che ha le coordinate espresse nel CRS del progetto.

Per esempio il problema si verificherebbe ugualmente se si utilizzasse la semplice espressione @map_extent_center per creare un punto al centro della mappa, nel caso in cui i CRS del layer e del progetto fossero diversi.

Quindi, bisogna inserire nell’espressione la trasformazione di coordinate…

with_variable(
  'center',
  if(
    @project_crs <> @layer_crs,
    transform(@map_extent_center, @project_crs, @layer_crs),
    @map_extent_center
  ),

  if(
    array_contains(
      array_slice(
        array_agg(
          @id,
          order_by := distance(@geometry, @center)
        ), 0, 7
      ),
      @id
    ),
    make_line(@geometry, @center),
    none
  )

)

Oppure più semplicemente:

with_variable(
  'center',
  transform(@map_extent_center, @project_crs, @layer_crs),

  if(
    array_contains(
      array_slice(
        array_agg(
          @id,
          order_by := distance(@geometry, @center)
        ), 0, 7
      ),
      @id
    ),
    make_line(@geometry, @center),
    none
  )

)

Ciao Andrea
ho registrato un video per farti notare qualcosa di strano

in breve, le espressioni che hai creato non funzionano al primo utilizzo, ma dal secondo in poi… strano, nel video lo spiego.

grazie

saluti

Ho provato a replicare il tuo stesso procedimento, ma non riscontro il problema…

ho provato con due diversi profili quasi nuovi,
il problema da me persiste solo se utilizzo OSM da plugin QMS se invece carico OSM dai tasselli XYZ tutto funziona.

Continuerò ad indagare

saluti

Nelle impostazioni del plugin QuickMapServices installato nel tuo profilo hai attivato l’opzione “Enable EPSG:3857 (OTF) on adding TMS”?

Se sì, puoi provare nuovamente dopo averla disattivata e creato un nuovo progetto?

Bingo,
sembra funzionare!!!

Complimenti
Devo ammettere che è stato avvincente (non scherzo, dico sul serio)

Piccola demo del little spider

saluti

Infatti si tratta di un bug dell’opzione “Enable EPSG:3857 (OTF) on adding TMS” del plugin QuickMapServices.
Il bug era stato segnalato vari anni fa Adding a QMS layer changes destination SRS and not project CRS, which is extremely confusing · Issue #214 · nextgis/quickmapservices · GitHub e poi chiuso dopo molti commenti e repliche degli sviluppatori. A quanto pare però il bug è sempre presente.

Quando quell’opzione è attiva e viene aggiunto un layer TMS/XYZ tramite QuickMapServices in un progetto con CRS, per esempio, EPSG:32632 (in cui c’è già un layer con lo stesso CRS EPSG:32632) la rappresentazione cartografica delle geometrie nella mappa viene impostata col CRS EPSG:3857, tuttavia il CRS del progetto non viene modificato e rimane EPSG:32632 creando quindi una discrepanza tra il CRS con cui viene visualizzata la mappa e il CRS del progetto/mappa che è mostrato in basso a destra nella GUI.
Ciò non accade se quell’opzione è disattivata o se si aggiunge un layer TMS/XYZ (o altri tipi di layer) in qualsiasi altro modo, cioè non usando QuickMapServices.

Quell’opzione è disattivata di default. Non se perché nelle tue installazioni era attiva.

Comunque, è possibile modificare l’espressione in modo che funzioni anche nel caso in cui il layer TMS/XYZ sia aggiunto tramite QuickMapServices con l’opzione “Enable EPSG:3857 (OTF) on adding TMS” attiva; basta cambiare la variabile @project_crs con @map_crs:

with_variable(
  'center',
  transform(@map_extent_center, @map_crs, @layer_crs),

  if(
    array_contains(
      array_slice(
        array_agg(
          @id,
          order_by := distance(@geometry, @center)
        ), 0, 7
      ),
      @id
    ),
    make_line(@geometry, @center),
    NULL
  )

)
1 Like

Per chi volesse replicare il ragnetto che insegue il mouse:

saluti

Ottima guida!

Per quanto riguarda l’espressione per visualizzare le etichette degli 8 elementi più vicini, penso che possa essere semplificata nel seguente modo:

with_variable(
  'center',
  transform(@canvas_cursor_point, @map_crs, @layer_crs),
  array_contains(
    array_slice(
      array_agg(
        @id,
        order_by := distance(@geometry, @center)
      ), 0, 7
    ),
    @id
  )
)