¿Encuentra la distancia mínima de borde a borde de polígonos usando ArcGIS Desktop?

9

Tengo un mapa de aproximadamente 3000 polígonos en ArcGIS 10. Estoy buscando encontrar la distancia entre cada uno de ellos. Sé cómo hacerlo usando las coordenadas lat y long del centroide, pero estoy buscando la distancia más corta en línea recta desde el borde más cercano de un polígono hasta el borde más cercano del otro polígono. ¿Algunas ideas?

Kevin
fuente

Respuestas:

11

Es un código agradable, pero no tan bueno como (suponiendo que su tabla esté en coordenadas geográficas, si no solo elimine los modelos a la geografía)

CREATE TABLE mytable_distances AS
SELECT a.id, b.id, ST_Distance(a.geom::geography, b.geom::geography) as distance
FROM mytable a, mytable b;

¿He mencionado que las bases de datos espaciales son geniales? Ellas hacen. Oh, ellos lo hacen.

Paul Ramsey
fuente
Esto encontrará la distancia entre los vértices más cercanos, pero no los bordes en sí, no parece que GEOS exponga esta respuesta más precisa. Aún así, bastante útil!
scw
1
Lo siento scw, te equivocas de muchas maneras. PostGIS tiene cálculos de distancia nativos. GOES no está involucrado en eso. En segundo lugar, proporciona la distancia más cercana entre los bordes, no solo los vértices tanto en la distancia de geometría como en el cálculo de la distancia esferoidea del tipo de geografía. Pablo lo escribió.
Nicklas Avén
Para verlo visualmente para la geometría, puede usar st_shortestline que devuelve la línea que da la distancia.
Nicklas Avén
1
Nik tiene razón, tanto en geometría como en geografía, la función de distancia devuelve la distancia entre los bordes. Por ejemplo, seleccione st_distance ('LINESTRING (0 0, 0 100)', 'LINESTRING (50 1, 51 1)')
Paul Ramsey
2
wow, las bases de datos espaciales hacen rock! Estoy calculando la distancia entre un conjunto de ~ 8200 polígonos y el vecino más cercano en otro conjunto de ~ 8400 polígonos. en arcgis 10, la herramienta 'generar tabla cercana' con un radio de búsqueda de 10000 m tomó 1 hora y 15 minutos (en el escritorio i7 quad-core de 3.4 GHz). La misma consulta en PostGIS solo tomó 3.5 minutos, y eso fue en una computadora más lenta (un MacBook Pro i7 de doble núcleo a 2.7 GHz).
pistachionut
8

La distancia de A a B es igual a B a A, y la distancia de A a A es cero, por lo tanto, una media matriz le ahorrará algo de trabajo.

IProximityOperator devuelve la distancia desde el borde. El siguiente código utiliza una proyección azimutal centrada en el centroide de cada polígono (también debería funcionar con líneas). Si los polígonos no son demasiado complejos (o si tiene mucha memoria) cargando todas las geometrías en la memoria, proyectarlas sería más rápido. (Esto no se ha probado a fondo).

public class Pair
{
    public int Oid1;
    public int Oid2;
    public double Dist;
    public static void TestGetDistances()
    {
        IWorkspaceFactory wsf = new ESRI.ArcGIS.DataSourcesGDB.FileGDBWorkspaceFactoryClass();

        string path = @"C:\Program Files\ArcGIS\DeveloperKit10.0\Samples\data\Usa\USA.gdb";
        var fws = wsf.OpenFromFile(path, 0) as IFeatureWorkspace;
        IFeatureClass fc = fws.OpenFeatureClass("states");
        var halfMatrix = Pair.GetPairs(fc);

    }
    /// <summary>
    /// key is oid of each feature, value is pairs for features with smaller oids.
    /// </summary>
    /// <param name="fc"></param>
    /// <returns></returns>
    public static SortedList<int, List<Pair>> GetPairs(IFeatureClass fc)
    {
        ISpatialReferenceFactory3 srf = new SpatialReferenceEnvironmentClass();
        IProjectedCoordinateSystem pcs = 
        srf.CreateProjectedCoordinateSystem((int)esriSRProjCSType.esriSRProjCS_WGS1984N_PoleAziEqui);

        var outList = new SortedList<int, List<Pair>>();
        IFeatureCursor fCur = fc.Search(null, true);
        IFeature f;
        while ((f = fCur.NextFeature()) != null)
        {
            var pairs = GetDistances(f, pcs);
            Debug.Print("{0} has {1} pairs", f.OID, pairs.Count);
            outList.Add(f.OID, pairs);
        }
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(fCur);
        return outList;
    }

    private static IPoint GetGCSCentroid(IGeometry geom)
    {
        if (geom.SpatialReference is IProjectedCoordinateSystem)
        {
            geom.Project(((IProjectedCoordinateSystem)geom.SpatialReference).GeographicCoordinateSystem);
        }
        IArea a = geom is IArea ? geom as IArea : geom.Envelope as IArea;
        return a.Centroid;
    }

    /// <summary>
    /// return a list of all other features whose OID is lesser than f1
    /// </summary>
    /// <param name="f1"></param>
    /// <param name="pcs"></param>
    /// <returns></returns>
    private static List<Pair> GetDistances(IFeature f1, IProjectedCoordinateSystem pcs)
    {
        IPoint centroid = GetGCSCentroid(f1.ShapeCopy);

        pcs.set_CentralMeridian(true, centroid.X);
        ((IProjectedCoordinateSystem2)pcs).LatitudeOfOrigin = centroid.Y;
        var g1 = f1.ShapeCopy;
        g1.Project(pcs);

        var outList = new List<Pair>();
        var fc = f1.Class as IFeatureClass;
        var proxOp = g1 as IProximityOperator;
        IFeatureCursor fCur = fc.Search(null, true);
        IFeature f2 = null;
        while ((f2 = fCur.NextFeature()) != null)
        {
            if (f2.OID < f1.OID)
            {
                var g2 = f2.ShapeCopy;
                g2.Project(pcs);
                outList.Add(new Pair()
                {
                    Oid1 = f1.OID,
                    Oid2 = f2.OID,
                    Dist = proxOp.ReturnDistance(g2)
                });
            }
        }
        System.Runtime.InteropServices.Marshal.FinalReleaseComObject(fCur);
        return outList;
    }
}
Kirk Kuykendall
fuente
Este es un buen código. No sabía sobre IproximityOperator, y terminé codificando algo como esto yo mismo (obviamente es más lento)
George Silva
2

Creo que la herramienta de tabla cercana funcionaría para lo que quieres:

Determina las distancias desde cada entidad en las entidades de entrada a una o más entidades cercanas en las entidades cercanas, dentro del radio de búsqueda. Los resultados se registran en la tabla de salida.

Simplemente deje el radio de búsqueda en blanco.

Jason Scheirer
fuente
Esta es la solución que probaría primero, pero necesita un nivel de licencia de ArcInfo para desbloquear la herramienta Generar tabla cercana (Análisis).
PolyGeo