sélectionner un pourcentage (75%) d'un groupe de points en fonction de la distance d'un point distinct dans ArcGIS?

9

Ceci est spécifique à ArcGIS.

J'ai 2 fichiers de formes de points Aet B, le premier ( A) est un seul point qui contient un lat long, le second ( B) est une multitude de points (plus de 12k) qui contiennent chacun leur lat et long. Ce que j'essaie de faire, c'est d'automatiser la sélection de 75% des Bpoints du fichier de formes en fonction de la distance par rapport au fichier de formes A. En d'autres termes, je veux sélectionner les 75% de Bpoints de shapefile les plus proches d'un point de shapefile A.

Furlong
la source
Une solution programmatique est-elle acceptable?
Kirk Kuykendall
BTW, j'ai demandé à Esri d'autoriser l'utilisation du Shapefield dans un ITableSortCallback personnalisé, mais on m'a dit qu'il n'y avait aucune justification à cela. Ce cas d'utilisation montre le contraire.
Kirk Kuykendall
@Kirk Kuykendall Oui, une solution programmatique serait en fait préférée car c'est un processus que je devrai répéter plus de 1k fois. J'ai environ 1200 points séparés et chacun de ces points a un autre fichier de formes avec en moyenne 12k points autour. Je dois trouver un moyen de sélectionner facilement les 75% de points environnants les plus proches pour tous. Le faire manuellement est tout simplement hors de question.
Furlong
Ce commentaire est peut-être en dehors de la portée appropriée d'un commentaire, mais quand et pourquoi une telle analyse serait-elle utile? C'est pour ma propre élucidation; pardonne ma lenteur.
Nathanus
1
Pensez à utiliser un logiciel statistique. Si vous deviez fusionner tous les 1200 fichiers de formes, créant un champ d'ID source pendant la fusion, vous pourriez joindre les coordonnées du point central correspondant à cela et calculer toutes les distances 1200 * 12k = 14,4M. Ce dont vous avez besoin est alors une liste des 75e centiles de distance par id source: cela prendrait environ dix secondes avec Stata (commercial) ou R (open source). (Si vous utilisez ArcGIS pour cela, faites-nous savoir combien de temps il faut pour le calcul. :-)
whuber

Réponses:

5

Vous pouvez créer un tampon en anneau multiple sur le fichier de formes A, puis faire une jointure spatiale du tampon vers le fichier de formes B. Lorsque vous effectuez une jointure spatiale de polygones et de points, vous obtenez un compte du nombre de points de chaque polygone de l'attribut table de la jointure. Ensuite, en examinant le nombre total de points dans les tampons, vous pouvez obtenir 75% des points dans le fichier de formes B.

Une approche légèrement différente serait de l'écrire en Python et de vérifier les 75% dans une boucle, mais s'il s'agit d'un calcul unique, vous n'en aurez peut-être pas besoin.

djq
la source
4
Il serait plus simple, plus rapide et plus précis d'effectuer une jointure spatiale de A à B, de calculer le troisième quartile du champ [distance] résultant et de sélectionner tous les enregistrements inférieurs à cette distance.
whuber
Je ne savais pas qu'il était possible de joindre des points dans l'espace! Je suis d'accord, ce serait une bien meilleure façon de procéder.
djq
@Andy Au contraire, la jointure est une relation de point le plus proche. Il n'est basé sur aucun attribut tabulé. De plus, dans le logiciel Arc * (pour revenir à ArcView 2), la distance est automatiquement calculée à la suite de la jointure.
whuber
1
@whuber, je sais! D'où la rétractation (instruction supprimée!) Je suppose que vous pouvez le faire en joignant les tables d'attributs (et en calculant la distance vous-même) mais cela serait inutile compte tenu du contexte. Je suppose que le point que je voudrais réitérer est qu'il s'agit simplement de calculer la distance entre 1 point, aucun bouclage ou tampon ou procédure itérative n'est nécessaire.
Andy W
1
@Furlong Si vous lisez l'exemple de jointure spatiale: help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//… vous pouvez vous faire une idée de comment exécuter ceci en python. Ensuite, il s'agit de parcourir la table attributaire et de choisir des valeurs qui correspondent à vos critères
djq
4

Pour 1200 points (ou même jusqu'à 12 millions de points?), Je les mettrais simplement en mémoire en tant que collection générique - dans ce cas, une liste triée de listes . Cela pourrait être simplifié en sautant simplement des points lorsque vous rencontrez une situation avec plusieurs points qui sont à la même distance du point d'origine. De plus, pour des performances, pensez à utiliser une table de hachage au lieu d'une SortedList et à trier une fois après avoir inséré toutes les distances. Cela prendrait encore quelques lignes de code (?).

Je n'ai pas eu le temps de tester cela, mais ce c # pourrait vous aider à démarrer:

private void SelectNTile(string layer1, string layer2, double nTile)
{
    var fLayer1 = FindLayer(ArcMap.Document.FocusMap, "LayerWithLotsofPoints");
    var fLayer2 = FindLayer(ArcMap.Document.FocusMap, "LayerWithOneSelectedPoint");
    IFeature feat = GetSingleFeature(fLayer2);
    var distList = MakeDistList(fLayer1.FeatureClass,(IPoint)feat.ShapeCopy);
    // assume not many points exactly same distance
    var nRecs = (int)(distList.Count * nTile); // nTile would be 0.75 for 75%
    var Oids = new List<int>();
    foreach (KeyValuePair<double, List<int>> kvp in distList)
    {
        Oids.AddRange(kvp.Value);
        if (Oids.Count > nRecs)
            break;
    }
    var fSel = fLayer1 as IFeatureSelection;
    var OidArray = Oids.ToArray();
    fSel.SelectionSet.AddList(Oids.Count, ref OidArray[0]);                
}

private SortedList<double, List<int>> MakeDistList(IFeatureClass fc, IPoint pnt)
{
    var outList = new SortedList<double, List<int>>();
    var proxOp = pnt as IProximityOperator;
    IFeatureCursor fCur = null;
    try
    {
        fCur = fc.Search(null, true); // recycling is faster, we just need OIDs
        IFeature feat;
        while ((feat = fCur.NextFeature()) != null)
        {
            double dist = proxOp.ReturnDistance(feat.Shape);
            if (!outList.ContainsKey(dist))
                outList.Add(dist, new List<int> { feat.OID });
            else
                outList[dist].Add(feat.OID);  // this should rarely happen
        }
    }
    catch
    {
        throw;
    }
    finally
    {
        if (fCur != null)
            System.Runtime.InteropServices.Marshal.FinalReleaseComObject(fCur);
    }
    return outList;
}
private IFeature GetSingleFeature(IFeatureLayer fLayer)
{
    var fSel = fLayer as IFeatureSelection;
    if (fSel.SelectionSet.Count != 1)
        throw new Exception("select one feature in " + fLayer.Name + " first");
    var enumIDs = fSel.SelectionSet.IDs;
    enumIDs.Reset();
    IFeature feat = fLayer.FeatureClass.GetFeature(enumIDs.Next());
    return feat;
}
private IFeatureLayer FindLayer(IMap map, string name)
{
    throw new NotImplementedException();
}
Kirk Kuykendall
la source
4

Un script de géotraitement Python est un choix évident:

  1. Utilisez l' outil Point Distance pour calculer la distance entre vos entités dans la classe d'entités B (le paramètre "Entités en entrée" de l'outil) et le point dans la classe d'entités A (le paramètre "Entités proches" de l'outil).
  2. Triez le tableau selon la distance calculée.
  3. Sélectionnez les 75 premiers% des ID d'objet dans le tableau de sortie (la colonne "Input_FID") et utilisez-les pour effectuer votre sélection à partir des entités d'origine de la classe d'entités B.
Philippe
la source
2

J'ai eu ce problème il y a quelques années. J'ai trouvé plus facile de garder les données en tant que `` données plates '', de parcourir toutes les données et de calculer manuellement la distance, puis de prendre les 75% supérieurs (j'ai en fait conservé les 10% supérieurs). J'ai ensuite fait la même chose dans ArcIMS en utilisant leurs calculs de distance et cela a pris beaucoup plus de temps.

La mise en mémoire tampon est un énorme frais généraux, mais les calculs mathématiques sont un point fort. Si vous mettez en mémoire tampon 12k points, je pense que vous allez avoir des problèmes de performances.

Poilu
la source
J'ai [@Mapperz] supprimé les commentaires - les lignes directrices des outils de mod ont signalé ce message parce qu'il s'est dégradé en querelles inutiles ...
Mapperz