Найти минимальное расстояние от края до края полигонов с помощью ArcGIS Desktop?

9

У меня есть карта около 3000 полигонов в ArcGIS 10. Я ищу, чтобы найти расстояние между каждым из них. Я знаю, как это сделать, используя координаты широты и долготы центроида, но я ищу самое короткое расстояние по прямой линии от ближайшего края одного многоугольника до ближайшего края другого многоугольника. Любые идеи?

Kevin
источник

Ответы:

11

Это хороший кусок кода, но не так хорошо, как (при условии, что ваша таблица в географических координатах, если не просто удалить приведения к географии)

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;

Я упоминал, что пространственные базы данных качаются? Они делают. Ох, они делают.

Пол Рэмси
источник
Это позволит найти расстояние между ближайшими вершинами, но не сами ребра - кажется, что GEOS не дает такой более точный ответ. Тем не менее, довольно удобно!
Scw
1
Извините, вы ошибаетесь во многих отношениях. PostGIS имеет собственные расчеты расстояния. GOES не участвует в этом. Во-вторых, он абсолютно дает самое близкое расстояние между ребрами, а не только вершины как в геометрическом расстоянии, так и в вычислении сфероидального расстояния географического типа. Пол написал это.
Никлас Авен
Чтобы увидеть это визуально для геометрии, вы можете использовать st_shortestline, который возвращает линию, которая дает расстояние.
Никлас Авен
1
Ник прав, и в геометрии, и в географии функция расстояния возвращает расстояние между краями. Например, выберите st_distance ('LINESTRING (0 0, 0 100)', 'LINESTRING (50 1, 51 1)')
Пол Рэмси
2
вау, пространственные базы данных качаются! Я рассчитываю расстояние между набором ~ 8200 полигонов и ближайшим соседом в другом наборе ~ 8400 полигонов. в Arcgis 10 инструмент «Сгенерировать рядом с таблицей» с радиусом поиска 10000 м занял 1 час 15 минут (на четырехъядерном настольном компьютере i7 с частотой 3,4 ГГц). тот же запрос в PostGIS занял всего 3,5 минуты, и это было на более медленном компьютере (двухъядерный MacBook Pro с тактовой частотой 2,7 ГГц).
фисташковый
8

Расстояние от A до B такое же, как от B до A, а расстояние от A до A равно нулю, поэтому полуматрица сэкономит вам немного работы.

IProximityOperator возвращает расстояние от края. В приведенном ниже коде используется азимутальная проекция с центром в центре тяжести каждого многоугольника (также должна работать со строками). Если полигоны не слишком сложны (или если у вас много памяти), загружая все геометрии в память, проецирование их будет быстрее. (Это не полностью проверено).

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;
    }
}
Кирк Куйкендалл
источник
это хороший кусок кода. Я не знал об IproximityOperator, и в конечном итоге я сам написал что-то вроде этого (очевидно, это медленнее)
Джордж Сильва
2

Я думаю, что инструмент ближайшего стола будет работать для того, что вы хотите:

Определяет расстояния от каждого объекта во входных объектах до одного или нескольких соседних объектов в ближайших объектах в пределах радиуса поиска. Результаты заносятся в выходную таблицу.

Просто оставьте радиус поиска пустым.

Джейсон Шайрер
источник
Это решение, которое я бы попробовал в первую очередь, но для разблокировки инструмента «Сгенерировать близкую таблицу (анализ)» необходим уровень лицензии ArcInfo.
PolyGeo