Identification systématique du champ joint dans ArcMap?

9

Est-il possible d'identifier par programme le champ de jointure utilisé pour regrouper en table deux ensembles de données dans ArcMap? J'utilise actuellement ArcGIS 10.0, SP5 et préférerais une solution ArcPy , mais je ne serais pas opposé à d'autres solutions, si une solution ArcPy n'est pas disponible.

Une méthode que j'ai essayée consistait à parcourir tous les champs et à rechercher un "baseName" correspondant, mais ce n'est qu'une "supposition éclairée" où vous espérez que les noms de champs dans les deux bases de données sont les mêmes.

Pour une représentation graphique de ce que je recherche, je veux essentiellement identifier le "Champ de jointure d'entrée" et le "Champ de jointure de sortie" comme on le voit dans la boîte de dialogue "Ajouter une jointure", mais après coup, bien sûr.

Comment identifier le "champ de jointure d'entrée" et le "champ de jointure de sortie"?

Il s'agit d'une question de balise à Est-ce qu'une «jointure» peut être détectée par programme? , mais dans ce cas, je souhaite étendre la fonctionnalité pour identifier le (s) CHAMP (s) utilisé (s) pour joindre les deux (ou plus) ensembles de données.

RyanKDalton
la source
Avec quelle version d'ArcGIS travaillez-vous? Et je suppose sur la base des balises que vous recherchez spécifiquement un moyen de le faire avec arcpy et non ArcObjects?
blah238 du
J'utilise actuellement ArcGIS 10.0, SP5. Et oui, je cherche / j'espère une solution ArcPy, mais je ne serais pas opposé à une solution ArcObjects, si c'est la seule alternative.
RyanKDalton
1
Voici une documentation éventuellement révélatrice: edndoc.esri.com/arcobjects/9.2/ComponentHelp/esriGeoDatabase/… Elle implique la pRelClass Il s'agit de la RelationshipClass utilisée pour définir les tables de jointure et les champs de jointure ainsi que la cardinalité. La méthode Open crée un nouveau RelQueryTable ou renvoie une référence à un RelQueryTable existant si cette classe a déjà été créée. Vous pouvez invoquer cette méthode et trouver la référence impliquant thepRelClass
lewis
@lewis, vous n'avez pas besoin d'utiliser l'objet d'usine pour obtenir une référence à un RelQueryTable existant - voir ma réponse.
blah238

Réponses:

7

Voici une approche ArcObjects, basée sur cet exemple , pour énumérer toutes les jointures sur une couche et répertorier leurs noms de table de destination et source et leurs clés primaire et étrangère:

  1. Obtenir une référence à un ILayerqui a une ou plusieurs jointures
  2. Cast the ILayertoIDisplayTable
  3. Cast la IDisplayTable.DisplayTablepropriété àIRelQueryTable
  4. Alors que le tableau actuel est un IRelQueryTable:
    1. Vérifier que les RelQueryTable« s DestinationTableet SourceTablepropriétés
    2. Inspectez les propriétés OriginPrimaryKeyet OriginForeignKeyde la IRelQueryTable.RelationshipClasspropriété.
    3. Mettre la table en cours au courant RelQueryTablede » SourceTablepropriété

Ce script Python (à l'aide de comtypes et de ce module d'assistance ) passera par toutes les jointures, de la plus récente à la plus ancienne, et imprimera les noms des tables de destination et source, la clé primaire d'origine et la clé étrangère d'origine pour chaque jointure:

from ESRICOMHelpers import * # helper module from https://gis.stackexchange.com/a/5082/753
esriArcMapUI = GetESRIModule("esriArcMapUI")
esriCarto = GetESRIModule("esriCarto")
esriGeoDatabase = GetESRIModule("esriGeoDatabase")

def listJoins(table):
    while CType(table, esriGeoDatabase.IRelQueryTable):
        relQueryTable = CType(table, esriGeoDatabase.IRelQueryTable)
        destTable = relQueryTable.DestinationTable
        sourceTable = relQueryTable.SourceTable
        destDataset = CType(destTable, esriGeoDatabase.IDataset)
        sourceDataset = CType(sourceTable, esriGeoDatabase.IDataset)
        relClass = relQueryTable.RelationshipClass
        print destDataset.Name, sourceDataset.Name, relClass.OriginPrimaryKey, relClass.OriginForeignKey
        table = sourceTable

if __name__ == "__main__":
    #app = GetCurrentApp() # Use if run in-process
    app = GetApp("ArcMap") # Use if run in a standalone script
    mxd = CType(app.Document, esriArcMapUI.IMxDocument)

    # Gets the first layer in the active data frame
    map = mxd.FocusMap
    lyr = map.Layer[0]

    # Need to get the "display table" to access the joins
    displayTable = CType(lyr, esriCarto.IDisplayTable).DisplayTable

    # List the layer's joined tables
    listJoins(displayTable)

Exemple de sortie, étant donné une couche source avec trois jointures:

join_table_3 master_fc_join_table_1_join_table_2 JOIN_ID_3 master_fc.MASTER_ID
join_table_2 master_fc_join_table_1 JOIN_ID_2 master_fc.MASTER_ID
join_table_1 master_fc JOIN_ID_1 MASTER_ID

Pour plus d'informations, voir Comment accéder à ArcObjects à partir de Python?

blah238
la source
Cela semble très prometteur. J'ai installé le package comtypes et ajouté le code de la fonction d'assistance, mais je reçois l'erreur "global name 'esriGeoDatabase' is not defined". Où / Comment faut-il le définir dans le code précédant la ligne while CType(table, esriGeoDatabase.IRelQueryTable)?
RyanKDalton
Je ne l'ai pas inclus, mais à un moment donné, vous devez importer les wrappers comtypes autour des bibliothèques d'objets ESRI spécifiques dont vous avez besoin. L'utilisation de mon module d'assistance est aussi simple que esriGeoDatabase = GetESRIModule("esriGeoDatabase").
blah238
Je l'ai Merci. Cela fonctionnera-t-il également pour les couches dans ArcMap? Je passe chaque couche de layerList = arcpy.mapping.ListLayers(mxd)dans le listJoins(table)code, mais il saute à la whiledéclaration.
RyanKDalton
Je ne pense pas que vous puissiez effectuer un cast entre des objets arcpy et des objets comtypes, vous devez donc obtenir la référence ILayer via ArcObjects. J'ai mis à jour le code pour inclure un exemple plus complet. Cela devrait pouvoir être utilisé à la fois dans et hors du processus en commentant / décommentant les lignes pertinentes.
blah238
Se rapprocher, merci pour la patience à travers ... maintenant, comment avez-vous envoyé le fichier de document ArcMap réel (* .mxd) que vous souhaitez consulter? app.Documentrevient avec'NoneType' object has no attribute 'Document'
RyanKDalton
1

Mettez toutes les données des champs dans des chaînes (après les avoir commandées), comparez-les avec une fonction fuzzycompare et sélectionnez ceux qui ont donné la meilleure correspondance ou la correspondance au-delà d'une certaine précision.

Cette solution est lorsque certaines données ne correspondent pas. Si vous pensez que les deux colonnes conviendraient toujours, alors commandez et comparez pour une correspondance parfaite avec une fonction de comparaison ordinaire.

Sous le radar
la source
0

Essaye ça:

  • Utilisez l' outil de transformation XSLT du jeu d' outils Métadonnées pour écrire un fichier de métadonnées xml / html pour le jeu de données en question.

    arcpy.XSLTransform_conversion(r'X:\temp\Scratch.gdb\fc_FeatureToPoint',"C:\Program Files\ArcGIS\Desktop10.1\Metadata\Stylesheets\ArcGIS.xsl", r'X:\temp\Metadata.html')
  • Utilisez un analyseur HTML pour lire le fichier de métadonnées et recherchez le champ de jointure à partir de l'historique de géotraitement de l'outil Join Field

  • Exemple de sortie de l'outil de transformation XSLT

Sortie de l'outil de transformation XSLT

Nxaunxau
la source
1
C'est une idée vraiment intelligente que je pensais très prometteuse, mais d'après mes tests, il semble que cela ne fonctionnera que si le fichier a été joint à l'aide de l'outil GP appelé "JoinField", car cela est écrit dans le cadre de l'historique du processus GP pour cette couche. Si l'utilisateur a créé une jointure via l'interface utilisateur, la ligne de processus JoinField n'existe pas dans le fichier de sortie. Bonne idée quand même!
RyanKDalton
1
De toute façon, je ne compterais pas sur l'historique GP. Nous essayons de le supprimer dès que possible car pour les processus récurrents, il monte rapidement dans une énorme quantité de données qui rend une classe d'entités presque inutilisable.
blah238
-1

Les noms des tables jointes sont dans l'objet IFeatureLayer - IFeatureLayerDefinition sous la forme d'une chaîne .. qui, je pense, contient probablement la jointure SQL et donc les noms de champ.

http://edndoc.esri.com/arcobjects/8.3/diagrams/Map%20Layer%20Object%20Model.pdf

Ou voulez-vous dire si vous ne pouvez pas accéder à cet objet?

AnserGIS
la source
IFeatureLayerDefinitionne contient pas le "join SQL", il a seulement une DefinitionExpressionpropriété exposant la requête de définition de la couche d'entités, si elle est définie, qui est une clause WHERE qui limite les lignes affichées.
blah238
Il a une RelationshipClasspropriété, cependant, mais je pense que cela ne révèle que la jointure la plus récente. Vous devrez utiliser à la IRelQueryTableplace pour les obtenir tous.
blah238
-2

pour trouver des champs correspondants quel que soit le nom du champ, vous pouvez faire quelque chose comme ceci:

import arcpy

fc = r"temp/RiversJoined.shp"

fldList1 = [f.name for f in arcpy.ListFields(fc)]
fldList2 =[g.name for g in arcpy.ListFields(fc)]

for f in fldList1:
    values1 = [f_row[0] for f_row in arcpy.da.SearchCursor(fc, (f))]
    for g in fldList2:
        values2 = [g_row[0] for g_row in arcpy.da.SearchCursor(fc,(g))]
        #compare field values
        #get names of matching fields
        if (fldList2.index(g) != fldList1.index(f) and values1 == values2):
            print "match: " + str(g) + " match: "+ str(f)
mwil
la source
hé à qui a frappé ma réponse: pouvez-vous me dire ce qui ne va pas avec ma réponse? Merci.
mwil
1
Cela ne semble pas répondre à la question posée. Même chose avec la réponse de comparaison floue. Les champs identiques (ou même vaguement similaires) n'ont aucune incidence sur leur utilisation ou non dans une jointure.
blah238