Le blog francophone consacré
aux technologies Esri

Utiliser les relations spatiales entre les couches dans vos expressions Arcade

Les récentes évolutions du langage d'expressions Arcade (ArcGIS Online de décembre dernier et prochaine version 10.7 d'ArcGIS Enterprise en mars prochain) permettent désormais la récupération, par relation spatiale, d'informations issues d'entités se trouvant dans d'autres couches de la carte. Ceci peut être très utile pour enrichir le contenus de vos étiquettes ou de vos fenêtres contextuelles (popups) en allant requêter dynamiquement des informations se trouvant dans d'autres couches de la carte.
  
  
Ci-dessous, quelques de cas d'usages dans lesquels ce type d'expression évoluée est particulièrement intéressant et peut éviter de lourds développements applicatifs:
  1. En cliquant sur un parc public, je souhaite afficher une fenêtre contextuelle avec ses caractéristiques mais aussi la liste des trois arrêts de bus les plus proches.
  2. En cliquant sur l'emprise de mes zones de projets immobiliers, je souhaite afficher la zone de réglementaire de PLU dans laquelle elle se trouve.
  3. En cliquant sur une parcelle cadastrale, je souhaite compter combien de bâtiments elle contient.
  4. En cliquant sur un bâtiment, je souhaite savoir si il se trouve à moins de 500 m. d'un monument historique.
  5. En cliquant sur une conduite d'eau, je veux afficher le nombre de parcelles qu'elle traverse.
  6. En cliquant sur ma ligne électrique, je souhaite connaître la proportion des différents type d'occupation des sols sur une bande de 100m le long de ma ligne.

Je ne reviendrai pas sur la manière d'ajouter une expression Arcade dans la calculatrice de champ, dans une fenêtre contextuelle ou dans les paramètres d'étiquettes, l'aide en ligne (ou cet article arcOrama) vous y aideront. En revanche, je vous propose de reprendre chacun de ces 6 exemples et de voir une expression Arcade répondant au besoin. S'agissant d'expressions parfois complexes, différentes variantes peuvent être envisagées, peut-être même plus optimisées que celles que je vous propose :-)


Exemple n°1:

On considère que votre carte contient une couche avec les parcs publics (la couche courante) et une seconde couche nommée "Arrêts de bus" qui contient les arrêts de bus. Cette dernière possède un champ "Nom" contenant le nom de l'arrêt.

L'expression Arcade pourra être la suivante:

var my_buffer = Buffer($feature, 10, 'kilometers');
var distances_arrets = [];
var i = 0;
for (var my_feature in Intersects(FeatureSetByName($map,"Arrêts de bus"),my_buffer))
{
  var d = Distance($feature,my_feature,'meters');
  distances_arrets[i] = { 'NOM': my_feature.Nom, 'DISTANCE': d };
  i++;
}

function comparer_distances(a,b){
  if (a['DISTANCE']
    return -1;
  if (a['DISTANCE']>b['DISTANCE'])
    return 1;
  return 0;
}

var distances_arrets_tries = Sort(distances_arrets, comparer_distances)
var resultat = 'Les 3 arrêts de bus les plus proches:';
for (var i=0; i<3 b="" i="">
{
    resultat += TextFormatting.NewLine + 
                distances_arrets_tries[i].NOM + 
                ' (' +
                Round(distances_arrets_tries[i].DISTANCE) + 
                'm.)'
}

return resultat;

 

Exemple n°2:

On considère que votre carte contient une couche avec les projets d'aménagement (la couche courante) et une seconde couche nommée "Zonage PLU" qui contient les zones réglementaires du Plan Local d'Urbanisme. Cette dernière possède un champ "Type_Zone" contenant le code de la zone réglementaire.

L'expression Arcade pourra être la suivante:

for (var my_zone in FeatureSetByName($map,"Zonage PLU")){
    if (Intersects($feature, Geometry(my_zone))){
        return my_zone["Type_Zone"];
    }
}


 
Exemple n°3:

On considère que votre carte contient une couche avec les parcelles cadastrales (la couche courante) et une seconde couche nommée "Bâtiments" qui contient les bâtiments du cadastre.

L'expression Arcade pourra être la suivante:

var intersected_features = Intersects(FeatureSetByName($map,"Bâtiments"), $feature);
var n=0;
for (var my_feature in intersected_features)
{
    var my_centroid = Centroid(my_feature);
    Console(my_centroid);
    if (Within(my_centroid,$feature)){
        n++;
    }
}
return Text(n,'#');

  

Exemple n°4:

On considère que votre carte contient une couche avec les bâtiments (la couche courante) et une seconde couche nommée "Monuments Historiques" qui contient les bâtiments classés monuments historiques.

L'expression Arcade pourra être la suivante:

var my_buffer = Buffer($feature, 500, 'meters');
var nb_monuments = Count(Intersects(FeatureSetByName($map,"Monuments Historiques"),my_buffer))
return Text(nb_monuments,"#")

  

Exemple n°5:

On considère que votre carte contient une couche avec les conduites d'un réseau de distribution d'eau (la couche courante) et une seconde couche nommée "Parcelles" qui contient les parcelles cadastrales.

L'expression Arcade pourra être la suivante:

var intersected_features = Intersects(FeatureSetByName($map,"Parcelles"), $feature);
return Text(Count(intersected_features),"#")

   
 
Exemple n°6:

On considère que votre carte contient une couche avec les lignes électriques (la couche courante) et une seconde couche nommée "Occupation du sol" qui contient l'occupation des sols décrite dans un champ "Code_Occup_Sol".

L'expression Arcade pourra être la suivante:

var my_buffer = Buffer($feature, 100, 'meters');
var area_buffer = Area(my_buffer,'meters';
var intersected_features = Intersects(FeatureSetByName($map,"Occupation du sol"),my_buffer)
var type_field_name = "Code_Occup_Sol";
var my_types = [];
var resultat = '';
var i=0;


for (var my_feature in intersected_features)
{
    my_types[i] = my_feature[type_field_name]
    i++;
}

var my_unique_types = Distinct(my_types);

for (var j=0; j < Count(my_unique_types); j++)
{
   var my_type = my_unique_types[j];
   my_type = Replace(my_type,"'","");
   var sqlQuery = type_field_name + ' LIKE \'%' + my_type + '%\'';
   var features_by_type = Filter(intersected_features,sqlQuery);
   var sum_area_of_my_type = 0;
   
   for (var my_feature in features_by_type)
   {
       sum_area_of_my_type += Area(Intersection(my_buffer,my_feature),'meters');
       
   }

   resultat +=  '- ' +
                my_type +
                ' à ' + 
                Text( 100 * sum_area_of_my_type / area_buffer),"#.#") + 
                '%' +
                TextFormatting.NewLine 
}

return resultat

    

En espérant que ces exemples vous donneront des idées pour créer d'autres expressions spatiales avec Arcade. N'hésitez pas à les adapter à vos contextes et à me communiquer vos cas d'usage via les commentaires au bas de cet article.
   

Partager cet article:

Rejoindre la discussion

    Les commentaires à propos de cet article:

8 comments :

Anonyme a dit…

Bonjour,
Merci pour ces exemples clairs et plus variés que ceux fournis initialement par Esri US.
Deux questions me viennent à l'esprit :
- Est-il possible d'utiliser ce type de formule Arcade pour la symbologie, par exemple pour représenter la couleur d'un bâti en fonction du PLU sur lequel il est situé ?
- Peut-on récupérer un attribut issu d'une table (reliée ou non), donc non spatiale en utilisant la fonction FeatureSetByName, par exemple pour afficher le nombre de comptes-rendus (ou la date du dernier compte-rendu) associés à une entité, ces comptes-rendus étant dans une table reliée à l'entité par un identifiant unique ?

Gaëtan Lavenu a dit…

Bonjour,

Merci pour vos retours.

En ce qui concerne l'utilisation de ces relations spatiales dans le contexte de symbologie des couches, la réponse est oui. L'exemple que vous indiquez autour du PLU est tout à fait possible.

En ce qui concerne l'accès à des données depuis une table reliée, ce n'est pas encore possible pour l'instant mais c'est dans la Road Map d'Arcade et cela va arriver assez rapidement.

Anonyme a dit…

Merci pour ces précisions,

En espérant que l'on ait cette fonctionnalité à temps pour la prochaine version Enterprise... pas sûr vu le timing

Anonyme a dit…

Bonjour,

En ce qui concerne la symbologie et Arcade sur AGOL, est-il également possible d'attribuer une symbologie différente pour une entité ligne par exemple en fonction de plusieurs plages d'échelles définies ?

Si oui, est-il possible d'attribuer d'abord un type de symbologie par représentation avec un symbole unique puis, pour une autre plage d'échelle, une symbologie avec des valeurs uniques en fonction d'un champ ?

Si oui, auriez-vous un exemple de code car je n'ai encore rien trouvé sur ce sujet ?

Merci.

Gaëtan Lavenu a dit…

Bonjour,

Les expressions Arcade peuvent effectivement intégrer l'échelle courante (la variable $scale) pour modifier, par exemple, la valeur retournée par l'expression et donc le rendu de l'entité. En revanche, une expression Arcade ne permettra pas de changer le type de symbologie de la couche à la volée.

Pour obtenir ce genre de comportement, la stratégie est de copier la couche dans la carte (ce qui bien entendu ne duplique en rien les données sources) et d'appliquer à chacune d'entre elle le type de symbologie souhaitée. Ensuite, il suffira de définir les plages d'échelles correspondantes pour chacune d'elle pour visualiser la couche souhaité selon l'échelle courante de la carte.

Anonyme a dit…

Bonjour,
Merci pour ces astuces et bouts de code très utiles,
Existe-il la fonction Distance avec la méthode euclidean et non plane ?

Par ailleurs, voici mon code pour l'exemple 1 car il n'est pas complet ou ne fonctionne pas en l'état (en tout cas chez moi), notamment pr la fct du comparateur :

var my_buffer = Buffer($feature, 800, 'meters');
var distances_arrets = [];
var i = 0;
for (var my_feature in Intersects(FeatureSetByName($map,"Bus TCL"),my_buffer))
{
var d = Distance($feature,my_feature,'meters');
distances_arrets[i] = { 'NOM': my_feature.Nom, 'DISTANCE': d };
i++;
}

function comparer_distances(a,b){
if (a['DISTANCE']b['DISTANCE'])
return 1;
return 0;
}

var distances_arrets_tries = Sort(distances_arrets, comparer_distances)
var resultat = 'Les 3 arrêts de bus les plus proches:';
for (var i in distances_arrets_tries)
{
resultat += TextFormatting.NewLine +
distances_arrets_tries[i].NOM +
' (' +
Round(distances_arrets_tries[i].DISTANCE) +
'm.)'
if (i==2) break;
}

return resultat;

Gaëtan Lavenu a dit…

Bonjour,

Il n'existe pas de fonction de calcul de distance géodésique (si c'est bien le sens de votre question). Cependant, si il s'agit de géométrie ponctuelle vous pouvez construire une polyligne (par exemple avec Polyline(json)) puis utiliser la fonction LengthGeodetic( geometry, unit).

Pour votre code, j'ai noté qu'il manque le signe "<" ou ">" dans le "if" de la fonction de comparaison, c'est peut être la raison de l'erreur.

Si vous rencontrez des problèmes avec Arcade et que vous pensez qu'il s'agit d'un dysfonctionnement, n'hésitez pas à contacter le support technique d'Esri France.

Anonyme a dit…

Bonjour
J'ai voulu comme précisé dans le commentaire réaliser une symbologie selon l'exemple 2 du PLU.
J'ai l impression que les fonctions n'existent pas dans les expressions arcade de la symbologie. Je ne peux pas faire appel a une autre couche de la carte ou a l'expression FeatureSetByName