Les étiquettes des points qui se chevauchent peuvent-elles être combinées / fusionnées en une seule étiquette?

12

J'ai des points représentant des exemples d'emplacements. Souvent, plusieurs échantillons seront prélevés au même emplacement: plusieurs points avec le même emplacement mais différents ID d'échantillons et d'autres attributs. Je voudrais étiqueter tous les points qui sont co-localisés avec une seule étiquette, avec du texte empilé répertoriant tous les exemples d'ID de tous les points à cet endroit.

Est-ce possible dans ArcGIS en utilisant le moteur d'étiquetage standard ou Maplex? Je sais que je pourrais contourner cela en créant une nouvelle couche avec tous les exemples d'ID pour chaque emplacement dans une valeur d'attribut, mais j'aimerais éviter de créer de nouvelles données uniquement pour l'étiquetage.

Fondamentalement, je veux partir de cela:

entrez la description de l'image ici

Pour cela (pour le point le plus haut):

entrez la description de l'image ici

Sans faire aucune édition manuelle des étiquettes.

Dan C
la source
Combien de points y a-t-il dans votre jeu de données?
Hornbydd

Réponses:

11

Une façon de procéder consiste à cloner la couche, à l'aide de requêtes de définition et à les étiqueter séparément, en utilisant uniquement la position d'étiquette en haut à gauche pour la première couche et en bas à gauche pour la seconde.

Ajoutez un entier de type THEFIELD au calque et remplissez-le à l'aide de l'expression ci-dessous:

aList=[]
def FirstOrOthers(shp):
 global aList
 key='%s%s' %(round(shp.firstPoint.X,3),round(shp.firstPoint.Y,3))
 if key in aList:
  return 2   
 aList.append(key)
 return 1

Appelez-le par:

FirstOrOthers( !Shape! )

Créez une copie de la couche dans la table des matières, appliquez la requête de définition THEFIELD = 1.

Appliquer la requête de définition THEFIELD = 2 pour la couche d'origine.

Appliquer un placement d'étiquette fixe différent

entrez la description de l'image ici

MISE À JOUR basée sur les commentaires de la solution d'origine:

Ajoutez le champ COORD et remplissez-le en utilisant

'%s %s' %(round( !Shape!.firstPoint.X,2),round( !Shape!.firstPoint.Y,2))

Résumez ce champ en utilisant le premier et le dernier pour l'étiquette. Joignez ce tableau à l'original à l'aide du champ COORD. Sélectionnez les enregistrements dans lesquels <<> dernier et concaténez la première et la dernière étiquette dans un nouveau champ à l'aide de

'%s\n%s' %(!Sum_Output_4.First_MUID!, !Sum_Output_4.Last_MUID!)

Utilisez Count_COORD et THEFIELD pour définir 2 «couches différentes» et des champs pour les étiqueter:

entrez la description de l'image ici

Mise à jour # 2 inspirée de la solution @Hornbydd:

import arcpy
def FindLabel ([FID],[MUID]):
  f,m=int([FID]),[MUID]
  mxd = arcpy.mapping.MapDocument("CURRENT")
  dFids={}
  dLabels={}
  lyr = arcpy.mapping.ListLayers(mxd,"centres")[0]
  with arcpy.da.SearchCursor(lyr,["FID","SHAPE@","MUID"]) as cursor:
    for row in cursor:
       FD,shp,LABEL=row
       XY='%s %s' %(round(shp.firstPoint.X,2),round( shp.firstPoint.Y,2))
       if f == FD:
         aKey=XY
       try:
          L=dFids[XY]
          L+=[FD]
          dFids[XY]=L
          L=dLabels[XY]
          L=L+'\n'+LABEL
          dLabels[XY]=L
       except:
          dFids[XY]=[FD]
          dLabels[XY]=LABEL
  Labels=dLabels[aKey]
  Fids=dFids[aKey]
  if f == Fids[0]:
    return Labels
  return ""

MISE À JOUR novembre 2016, espérons-le dernier.

L'expression ci-dessous testée sur 2000 doublons fonctionne comme un charme:

mxd = arcpy.mapping.MapDocument("CURRENT")
lyr = arcpy.mapping.ListLayers(mxd,"centres")[0]
dFids={}
dLabels={}
fidKeys={}
with arcpy.da.SearchCursor(lyr,["FID","SHAPE@","MUID"]) as cursor:
 for FD,shp,LABEL in cursor:
  XY='%s %s' %(round(shp.firstPoint.X,2),round( shp.firstPoint.Y,2))
  fidKeys[FD]=XY
  if XY in dLabels:
   dLabels[XY]+=('\n'+LABEL)
   dFids[XY]+=[FD]
  else:
   dLabels[XY]=LABEL
   dFids[XY]=[FD]

def FindLabel ([FID]):
  f=int([FID])
  aKey=fidKeys[f]
  Fids=dFids[aKey]
  if f == Fids[0]:
    return dLabels[aKey]
  return "
FelixIP
la source
Hé tu l'as craqué! Agréable! Je savais qu'il y avait quelqu'un de sifflant là-bas! Comme je m'y attendais, c'est un processus très itératif, donc exécutez sur un grand ensemble de données et les étiquettes prennent pour toujours à dessiner (eh bien, cela a été fait sur mes données de test). J'ai modifié votre style de code en développant quelques lignes. Je trouve le minimaliste, le tout sur une même ligne difficile à suivre.
Hornbydd
1
@Hornbydd merci pour les modifications. Cet écrou était difficile à casser en raison du comportement de l'étiquette (moteur?). Il traite tous les paramètres de fonction comme des chaînes! C'est pourquoi le premier IF n'a pas fonctionné sans f = int ([FID]). Concernant la vitesse je ne l'utiliserais jamais sur un set supérieur à 50 points. Il doit être converti en script qui remplit le nouveau champ en parcourant le jeu de données seulement deux fois: 1) le curseur de recherche pour compiler les deux dictionnaires, 2) le curseur de mise à jour pour les lire REMARQUE: les 3 premières lignes après l'instruction try sont obsolètes, après avoir publié la solution I réalisé qu'ils peuvent être retirés en toute sécurité.
FelixIP
Pour info, je n'ai pas eu l'occasion d'y revenir, mais je prévois de faire tourner votre solution dans la semaine prochaine. Il existe également une autre solution de contournement (celle que j'ai utilisée dans ce cas) sans Python, je posterai également une réponse à ce sujet.
Dan C
Je suis tombé sur cette page sur geonet. J'ai aimé l'utilisation intelligente des dictionnaires mondiaux, puis j'ai pensé à cette Q&R et y ai ajouté un lien.
Hornbydd
@Hornbydd oui, il est très intelligent et détaillé comme tout de Richard et fera une énorme différence pour ma (la nôtre) solution. On peut encore être amélioré en supprimant quelques lignes, dont la toute première. Cependant, sur la base du temps de réponse de l'OP, il semble qu'il ait perdu tout intérêt, je ne me dérange pas non plus
FelixIP
4

Voici une solution partielle.

Cela va dans l'expression de l'étiquette Advance. Ce n'est pas très efficace, donc je demande le nombre de points dans votre jeu de données. Donc, pour chaque ligne qui est étiquetée, elle crée 2 dictionnaires doù la clé est le XY et la valeur est le texte et d2qui est l'ID d'objet et le XY. En utilisant cette combinaison de dictionnaires, il est capable de renvoyer une seule étiquette qui est une concaténation avec des caractères de nouvelle ligne, dans mon exemple, elle concatène TARGET_FID. "sj" est le nom de la couche dans la table des matières.

import arcpy
def FindLabel ( [OBJECTID] ):
  ob = str([OBJECTID])
  mxd = arcpy.mapping.MapDocument("CURRENT")
  d ={}
  d2 = {}
  lyr = arcpy.mapping.ListLayers(mxd,"sj")[0]
  with arcpy.da.SearchCursor(lyr,["OID@","SHAPE@XY","TARGET_FID"]) as cursor:
    for row in cursor:
      objID = str(row[0])
      temp = row[1]
      tup = str(temp[0]) + "," + str(temp[1])
      d2[objID] = tup
      txt = str(row[2])
      if tup in d:
        v = d[tup] 
        v = v + "\n" + txt
        d[tup] = v
      else:
        d[tup] = txt  
  temp = d2[ob]
  return d[temp]

Pourquoi il s'agit d'une solution partielle, c'est que cela est fait pour chaque point, je n'ai pas pu imaginer comment vous désactiveriez tous les autres points empilés. C'est à cause de cela que je pense que la solution ultime est un python qui construit une nouvelle couche de points uniques avec une seule étiquette construite à partir de la pile de points.

Vous trouverez ci-dessous la sortie de 3 points empilés, comme vous pouvez le voir, l'étiquette est créée pour chaque point car ils existent tous au même emplacement.

Exemple

Hornbydd
la source