Jak sprawdzić, czy FeatureLayer już istnieje?

9

Obecnie pracuję nad projektem, w którym, gdy użytkownik ładuje swoją mapę (MXD) do naszego systemu, tworzymy dla nich kilka niestandardowych elementów do gry. Mój problem polega na tym, że nie mam pojęcia, jak sprawdzić, czy JUŻ JUŻ utworzyłem już te warstwy (powiedzmy, że użytkownik ładuje mxd, warstwy utworzone, zapisz, ponownie załaduj mxd, powinien sprawdzić, czy warstwy już istnieją).

Czy istnieje unikalny identyfikator FeatuerLayerClass w ArcEngine10, istnieją OIDName i ObjectClassID w FeatureLayerClass.FeatureClass, ale te wydają się nie działać (nie mogą przypisać ObjectClassId i chcą użyć UniqueId dla OIDName)?

Warstwę utworzyłem jako obiekt biznesowy klasy operacji w taki sposób.

Kod:

    /// <summary>
    ///     Unique Route LayerId
    /// </summary>
    public static Guid RouteFeatureLayerId
    {
        get { return Guid.Parse("ba25a332-0e48-4ce5-a4c5-38dc36c0700c"); }
    }

    /// <summary>
    ///     Feature class that stores info on the routes
    /// </summary>
    public FeatureLayerClass RouteFeatureLayer
    {
        get
        {
            if (_routeFeatureClass == null)
            {
                IPropertySet property = new PropertySetClass();
                property.SetProperty("Id", RouteFeatureLayerId);

                _routeFeatureClass = new FeatureLayerClass();
                _routeFeatureClass.FeatureClass = CreateFeatureClass(Workspace, null, ShapeType.Polylines.ToString(), CreateFields(ShapeType.Polylines, FeatureLayerType.Routes), null, null, "");
                _routeFeatureClass.Name = "Routes";
                _routeFeatureClass.Visible = true;
                _routeFeatureClass.Cached = true;
                _routeFeatureClass.AddExtension(property);
                CustomLayers.Add(_routeFeatureClass); 

            }

            return _routeFeatureClass;
        }
        set
        {
            _routeFeatureClass = value;
        }
    }

Tworzenie przestrzeni roboczej

    /// <summary>
    ///     Create a workspace for the shapefile or geodatabase
    /// </summary>
private IWorkspace CreateWorkspace(string workspaceType, string workspaceDirectory)
{
    Type factoryType = null;
    IWorkspaceFactory workspaceFactory = null;

    switch (workspaceType)
    {
        case "Shapefile":
            // Instantiate a Shapefile workspace factory
            factoryType = Type.GetTypeFromProgID("esriDataSourcesFile.ShapefileWorkspaceFactory");
            break;
        case "PersonalGeodatabase":
            // Instantiate an Access workspace factory
            factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.AccessWorkspaceFactory");
            break;
        case "FileGeodatabase":
            // Instantiate a file geodatabase workspace factory
            factoryType = Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory");
            break;
    }

    workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(factoryType);

    //Create a directory hierarchy to seperate out datasets created for Points, Polylines, and Polygons
    Directory.CreateDirectory(workspaceDirectory);

    IWorkspaceName workspaceName = workspaceFactory.Create(workspaceDirectory + "\\", workspaceType, null, 0);
    IName Name = (IName)workspaceName;
    IWorkspace workspace = (IWorkspace)(Name.Open());
    return workspace;

}

Tworzenie FeatureClass

        /// <summary>
        ///     Helper to create a Feature Class.
        /// </summary>
        private IFeatureClass CreateFeatureClass(IWorkspace workspace, IFeatureDataset featureDataset, string featureClassName, IFields fields, ESRI.ArcGIS.esriSystem.UID CLSID, ESRI.ArcGIS.esriSystem.UID CLSEXT, string configKeyword)
        {
            IFeatureClass featureClass = null;
            IFeatureWorkspace featureWorkspace = (IFeatureWorkspace)workspace; // Explicit Cast
            string shapeFieldName = String.Empty;

            try
            {
                if (featureClassName == "")
                {
                    return null; // name was not passed in
                }
                //else if (((IWorkspace2)workspace).get_NameExists(esriDatasetType.esriDTFeatureClass, featureClassName))
                //{
                //    featureClass = featureWorkspace.OpenFeatureClass(featureClassName); // feature class with that name already exists
                //    return featureClass;
                //}

                // assign the class id value if not assigned
                if (CLSID == null)
                {
                    CLSID = new ESRI.ArcGIS.esriSystem.UIDClass();
                    CLSID.Value = "esriGeoDatabase.Feature";
                }

                // locate the shape field
                for (Int32 j = 0; j < fields.FieldCount; j++)
                {
                    if (fields.get_Field(j).Type == esriFieldType.esriFieldTypeGeometry)
                    {
                        shapeFieldName = fields.get_Field(j).Name;
                    }
                }

                // finally create and return the feature class
                if (featureDataset == null)
                {
                    // if no feature dataset passed in, create at the workspace level
                    featureClass = featureWorkspace.CreateFeatureClass(featureClassName, fields, CLSID, CLSEXT, esriFeatureType.esriFTSimple, shapeFieldName, configKeyword);
                }
                else
                {
                    featureClass = featureDataset.CreateFeatureClass(featureClassName, fields, CLSID, CLSEXT, esriFeatureType.esriFTSimple, shapeFieldName, configKeyword);
                }
            }
            catch (Exception ex)
            {
                Debug.Assert(false, ex.ToString());
                Logger.Log.Debug(ex);
            }
            return featureClass;

        }

Kod, aby uzyskać warstwę

            /// <summary>
            ///     Finds the layer
            /// </summary>
            /// <returns>the subcatchment layer</returns>
            private IGeoFeatureLayer GetLayer(FeatureLayerClass featureLayer)
            {
                IGeoFeatureLayer layer = null;
                ILayerExtensions layerExtension;

                for (int x = 0; x < MapControl.LayerCount; x++)
                {
                    layerExtension = ((ILayerExtensions)MapControl.get_Layer(x));

                    if (featureLayer.ExtensionCount > 0 && layerExtension.ExtensionCount > 0 &&
                        layerExtension.get_Extension(0) is PropertySetClass &&
                        featureLayer.get_Extension(0) is PropertySetClass &&
                        ((PropertySetClass)layerExtension.get_Extension(0)).GetProperty("Id") == ((PropertySetClass)featureLayer.get_Extension(0)).GetProperty("Id"))
                    {
                        layer = MapControl.get_Layer(x) as IGeoFeatureLayer;
                        break;
                    }
                }

                return layer;
            }

Dzięki i pozdrawiam, Kevin

Kev84
źródło

Odpowiedzi:

7

Klasy obiektów i klasy obiektów mają swoje identyfikatory, które są unikalne w obrębie jednej geobazy. To bardzo często spełnia większość scenariuszy podobnych do twojego.

Jeśli nie możesz jednoznacznie zidentyfikować warstwy na podstawie jej klasy obiektów, możesz wykorzystać rozszerzenia warstw do przechowywania dowolnych danych z warstwą.

Rozszerzenie warstwy można dodać do warstwy za pomocą interfejsu ILayerExtensions . Obecnie nie ma wspólnego interfejsu dla rozszerzeń warstw, ale zazwyczaj implementują one pewną trwałość za pośrednictwem IPersistStream . Rozszerzenie warstwy nie zrobiłoby nic specjalnego, ale przechowywałoby dane, dzięki którym można jednoznacznie zidentyfikować dodaną warstwę.

Twoim zadaniem byłoby więc:

  • Utwórz klasę COM, która przechowa twoją flagę (lub jakiś wygenerowany identyfikator). Zaimplementuj IPersistStream dla tej klasy. EDYCJA: możesz łatwo użyć PropertySet jako obiektu rozszerzenia warstwy, zamiast tworzyć własną klasę.
  • Gdy dodajesz warstwę, przejrzyj wszystkie warstwy na mapie i sprawdź, czy do którejś z nich przypisano rozszerzenie warstwy, z oczekiwanymi przechowywanymi danymi.
  • W takim przypadku nie dodawaj warstwy, ponieważ jest już obecna.
  • Jeśli nie, dodaj warstwę i dodaj do niej wystąpienie rozszerzenia warstwy za pomocą ILayerExtensions.

Miałem bardzo podobny problem, a rozszerzenia warstw okazały się najlepiej dopasowane.

EDYCJA: poniżej zamieszczam kod dla klasy statycznej pomocnika, który pozwala szybko pracować z właściwościami ustawionymi w zestawie właściwości przechowywanym w rozszerzeniu warstwy (wymagany .NET 3.5 lub wyższy). Dba o dostęp do obiektu rozszerzenia i utworzenie go, jeśli nie jest już przypisany do warstwy. Używa się go w następujący sposób:

        // 1) is a particular property ("MY.KEY") set on a layer?
        var isPropertySet = PropertySetLayerExtensionHelper.ExtensionPropertySetContainsKey(layer, "MY.KEY");

        // 2) set a property with a value on the layer:
        PropertySetLayerExtensionHelper.ExtensionPropertySetSetValueForKey(layer, "MY.KEY", "SomeValue");

        // 3) retrieve a value for the given key stored at some point before:
        var value = PropertySetLayerExtensionHelper.ExtensionPropertySetGetValueForKey(layer, "MY.KEY");

Zamiast „SomeValue” prawdopodobnie wygenerujesz i zapiszesz tam jakiś rodzaj identyfikatora warstwy.

Oto pełny kod źródłowy dla PropertySetLayerExtensionHelperklasy:

public static class PropertySetLayerExtensionHelper
{
    /// <summary>
    /// Returns whether the property set stored in the layer extensions contains a value for the given key.
    /// </summary>
    /// <param name="layer">The layer.</param>
    /// <param name="key">The key.</param>
    /// <returns>Whether the property set stored in the layer extensions contains a value for the given key.</returns>
    public static bool ExtensionPropertySetContainsKey(ILayer layer, string key)
    {
        if (layer == null) throw new ArgumentNullException("layer");
        if (key == null) throw new ArgumentNullException("key");

        var propertySet = GetPropertySetInLayerExtension(layer);
        return propertySet != null
            && propertySet.AsEnumerable().Any(pair => pair.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
    }

    /// <summary>
    /// Returns the value for the given key from the property set stored in the layer extension or <b>null</b>
    /// if no such key is present.
    /// </summary>
    /// <param name="layer">The layer.</param>
    /// <param name="key">The key.</param>
    /// <returns>The value for the given key from the property set stored in the layer extension or <b>null</b>
    /// if no such key is present.</returns>
    public static object ExtensionPropertySetGetValueForKey(ILayer layer, string key)
    {
        if (layer == null) throw new ArgumentNullException("layer");
        if (key == null) throw new ArgumentNullException("key");

        var propertySet = GetPropertySetInLayerExtension(layer);
        if (propertySet == null) return null;

        return propertySet.AsEnumerable()
            .Where(p => p.Key.Equals(key, StringComparison.OrdinalIgnoreCase))
            .Select(p => p.Value)
            .FirstOrDefault();
    }

    /// <summary>
    /// Sets the value for the given key in the property set stored in a layer extension. If there is
    /// no property set among the layer's extensions, it is created and assigned to the layer.
    /// </summary>
    /// <param name="layer">The layer.</param>
    /// <param name="key">The key.</param>
    /// <param name="value">The value for the given key.</param>
    public static void ExtensionPropertySetSetValueForKey(ILayer layer, string key, object value)
    {
        if (layer == null) throw new ArgumentNullException("layer");
        if (key == null) throw new ArgumentNullException("key");

        var propertySet = GetOrCreatePropertySetInLayerExtension(layer);
        if (propertySet == null)
        {
            throw new InvalidOperationException("The given layer does not support layer extensions.");
        }

        propertySet.SetProperty(key, value);
    }

    /// <summary>
    /// Returns a property set from a layer extension.
    /// </summary>
    /// <param name="layer">The layer.</param>
    /// <returns>A property set from a layer extension.</returns>
    public static IPropertySet GetPropertySetInLayerExtension(ILayer layer)
    {
        if (layer == null) throw new ArgumentNullException("layer");

        var layerExtensions = layer as ILayerExtensions;
        if (layerExtensions == null)
        {
            return null;
        }

        var propertySetExtension = layerExtensions.AsEnumerable().OfType<IPropertySet>().FirstOrDefault();
        return propertySetExtension;
    }

    /// <summary>
    /// Returns a property set from a layer extension. If not set on the layer,
    /// the property set is created and assigned to it.
    /// </summary>
    /// <param name="layer">The layer.</param>
    /// <returns>A property set from a layer extension.</returns>
    public static IPropertySet GetOrCreatePropertySetInLayerExtension(ILayer layer)
    {
        if (layer == null) throw new ArgumentNullException("layer");

        var propertySet = GetPropertySetInLayerExtension(layer);
        if (propertySet != null)
        {
            return propertySet;
        }

        var layerExtensions = layer as ILayerExtensions;
        if (layerExtensions == null)
        {
            return null;
        }

        propertySet = new PropertySetClass();
        layerExtensions.AddExtension(propertySet);
        return propertySet;
    }

    private static IEnumerable<object> AsEnumerable(this ILayerExtensions layerExtensions)
    {
        if (layerExtensions == null) throw new ArgumentNullException("layerExtensions");

        for (var i = 0; i < layerExtensions.ExtensionCount; i++)
        {
            yield return layerExtensions.get_Extension(i);
        }
    }

    private static IEnumerable<KeyValuePair<string, object>> AsEnumerable(this IPropertySet propertySet)
    {
        if (propertySet == null) throw new ArgumentNullException("propertySet");
        if (propertySet.Count == 0) yield break;

        object names;
        object values;

        propertySet.GetAllProperties(out names, out values);

        var nameArray = (string[])names;
        var valueArray = (object[])values;

        for (var i = 0; i < nameArray.Length; i++)
        {
            yield return new KeyValuePair<string, object>(nameArray[i], valueArray[i]);
        }
    }
}
Petr Krebs
źródło
Czasami można uniknąć przechowywania tylko czegoś takiego jak IPropertySet ze specjalnym kluczem w ILayerExtension. Ponieważ jest to powszechna „sztuczka”, programiści powinni sprawdzić, czy istnieje IPropertySet przed jego dodaniem.
James Schek
@James: dobra wskazówka, zaktualizuję odpowiedź.
Petr Krebs,
+1, kiedy ostatnio sprawdzałem, Esri honoruje tylko IPersistStream - nie IPersistVariant - za rozszerzenia warstw. Nie jestem pewien dlaczego. Poprosiłem o wsparcie IPersistVariant jako ulepszenie, ale nie jestem pewien, czy kiedykolwiek zostało wdrożone. W każdym razie możesz użyć posta IPersistStream Richiego Carmichaela do przykładowego kodu.
Kirk Kuykendall
Rzeczą, która doprowadza mnie do szaleństwa przy korzystaniu z IPersistStream, jest to, że nie działa z dodatkami. Obiekt dodany do ILayerExtensions musi być COM CoCreatable.
James Schek
@Kirk: właśnie tak, pamiętam, że nie byłem w stanie zaimplementować trwałości rozszerzenia warstwy w VB. Dziękuję za poprawienie mnie.
Petr Krebs