Artikel getaggt mit ‘ArcSDE’

Mai 30th, 2011

ArcSDE lesen mit OpenSource GIS in .NET

In diesem Artikel möchte ich zeigen, wie man Geometrieobjekte aus einer ESRI ArcSDE-Datenbank mit einem OpenSource GIS Client visualisieren lassen kann. Dieser Ansatz ist nicht komplett quelloffen, da sich die verwendete Feature Data Object (FDO) API auch der proprietären ArcSDE API bedienen. Als GIS-Viewer wird die Bibliothek DotSpatial eingesetzt. In einer vor kurzem veröffentlichten Artikelserie habe ich beschrieben, wie man die FDO API mit DotSpatial in C# als Datenprovider verwenden kann. Als beispielhafte Datenquelle diente ein ESRI Shapefile.

Bei einer ArcSDE als Datenquelle wirds nur leicht anders. Das ist das Schöne an der FDO API: wenn man das Konzept und die Vorgehensweise einmal begriffen hat, ist die Verwendung der FDO API unabhängig vom Datenprovider. Nur einige wenige Dinge ändern sich.

Voraussetzungen
Die FDO API ist bezüglich des Datenproviders für eine ArcSDE abhängig von der proprietären ESRI ArcSDE API. Die entsprechenden DLLs von ESRI müssen deshalb im Systempfad zu finden sein. Eine detailliertere Beschreibung findet sich auf den Seiten der FDO API. Ein kurzer Auszug der Informationen, die sich zwar auf ArcGIS 9.1 beziehen, jedoch im wesentlichen auch für 9.3.1 gelten.

The operation of FDO Provider for ArcSDE is dependent on the presence of ArcSDE 9 and a supported data source, such as Oracle 9i, in the network environment. The host machine running FDO Provider for ArcSDE must also have the required DLLs present, which are available by installing either an ArcGIS 9.1 Desktop application or the ArcSDE SDK. For example, the required DLLs are present if either ArcView®, ArcEditor®, or ArcInfo® are installed. For more information about ArcGIS 9.1 Desktop applications and the ArcSDE SDK, refer to the ESRI documentation.

Specifically, in order for FDO Provider for ArcSDE to run, three dynamically linked libraries, sde91.dll, sg91.dll, and pe91.dll, are required and you must ensure that the PATH environment variable references the local folder containing these DLLs. For example, in Microsoft Windows, if ArcGIS 9.1 Desktop is installed to C:\Program Files\ArcGIS, then the required ArcSDE binaries are located at C:\Program Files\ArcGIS\ArcSDE\bin. Similarly, if the ArcSDE SDK is installed to the default location, then the required ArcSDE binaries are located at C:\ArcGis\ArcSDE\bin. The absence of this configuration may cause the following exception message „The ArcSDE runtime was not found.“.

Einzige Veränderung gegenüber der Version von 9.1 ist mittlerweile, dass die benötigten DLLs wohl nun im Verzeichnis „C:\Program Files\ArcGIS\Bin“ liegen. Das Verzeichnis ArcSDE gibt es auf der Clientseite (ArcGIS Desktop-Installation) nicht mehr.

Datenprovider-String

Der Datenprovider für die ArcSDE muss wie bei jedem anderen FDO-Provider auch dem ConnectionManager als Argument mitgeteilt werden.

FDORegistry = FeatureAccessManager.GetProviderRegistry();
FDOManager = FeatureAccessManager.GetConnectionManager();
FDOConnection = FeatureAccessManager.GetConnectionManager().CreateConnection("OSGeo.ArcSDE.3.6");

Verbindungsparameter

Die Verbindungsparameter sind im Gegensatz zum ESRI Shapefile etwas umfangreicher. Es müssen die im Datenbankbereich üblichen Verbindungseigenschaften folgendermaßen gesetzt werden:

IConnectionPropertyDictionary connectionPropertyDictionary;
connectionPropertyDictionary = FDOConnection.ConnectionInfo.ConnectionProperties;
connectionPropertyDictionary.SetProperty("Server","localhost");
connectionPropertyDictionary.SetProperty("Instance","5151");
connectionPropertyDictionary.SetProperty("Username","gisuser");
connectionPropertyDictionary.SetProperty("Password","password");
connectionPropertyDictionary.SetProperty("Datastore","Default Datastore");

Wobei die sog. Datastore zunächst einmal auf „Default Datastore“ gesetzt werden kann. Auf der Seite der FDO API findet man folgende Info dazu:

An ArcSDE data source may contain more than one data store. For the first call to Open(), a data store name is optional. If successful, the first call to Open() results in the data store parameter becoming a required parameter and a list of the names of the data stores in the data source becoming available. You must choose a data store and call Open() again.

Das Auswählen der Datastore funktioniert in C#-Code übersetzt folgendermaßen:

string value;
bool isRequired;
bool isEnumerable;
string[] strArry = null;
IConnectionPropertyDictionary dict = FDOConnection.ConnectionInfo.ConnectionProperties;
foreach (string st in dict.PropertyNames) {
	value = dict.GetProperty(st);
	isRequired = dict.IsPropertyRequired(st);
	isEnumerable = dict.IsPropertyEnumerable(st);

	if (isEnumerable && st == "Datastore")
	{
		strArry = new string[dict.EnumeratePropertyValues(st).Length];
		dict.EnumeratePropertyValues(st).CopyTo(strArry,0);
	}
}

Damit haben wir schon alles, was wir für den ArcSDE-Zugriff mit der FDO API in .NET brauchen. Das Verfahren, die Attributdaten und die Geometriedaten per FeatureReader zu lesen und in ein DotSpatial FeatureSet zu übersetzen ist identisch mit dem Verfahren beim ESRI Shapefile in den Artikeln 1,2 und 3.

Eine Besonderheit noch gegenüber dem Auslesen von Shapefiles ist natürlich, das eine ArcSDE mehrere Schemata besitzen kann, in denen widerum mehrere Feature Classes vorhanden sind. Mit der FDO API geht man jedoch nicht den hierarchischen Weg von Datenbank -> Schema -> Feature Class.

Vielmehr ist es hier so, dass die Feature Classes einen voll qualifizierenden Namen bekommen. Das heisst, die Feature Classes werden mit der FDO API nach folgendem Muster zugänglich gemacht, wenn man sich erfolgreich zur Datenbank verbunden hat:

<SCHEMA>:<FEATURE CLASS>

Ein Beispiel:

GISDYN:WEGE“ steht für die Feature Class „WEGE“, die im Schema „GISDYN“ angesiedelt ist. Beim setzen des Feature Class Namens sollte dies also berücksichtigt werden.

Mit dieser Info versteht man auch den unten beigefügten Code der Methode ListTablesOfDB() besser.

Versionierten SDE-Zugriff habe ich jedoch nicht getestet. Der standardmäßige Zugriff erfolgt wohl über die Version SDE.DEFAULT.

Als Anhang noch hier kompletten Code zum Zugriff auf eine ArcSDE über die FDO API:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using OSGeo.FDO;
using OSGeo.FDO.Connections;
using OSGeo.FDO.ClientServices;
using OSGeo.FDO.Geometry;
using OSGeo.FDO.Commands;
using OSGeo.FDO.Commands.Feature;
using OSGeo.FDO.Expression;
using OSGeo.FDO.Commands.DataStore;
using OSGeo.FDO.Commands.Schema;
using OSGeo.FDO.Filter;
using OSGeo.FDO.Schema;
using DotSpatial.Data;

namespace GSharpDotSpatial
{

	public class ArcSDEHelper
    {
		private string hostname;
		private int port;
		private string user;
		private string password;
		private string datastore;
		private string connStr;
		private string pkeyColumn;
		private DataTable dataTable;

		private IConnection FDOConnection;
        private IProviderRegistry FDORegistry;
        private IConnectionManager FDOManager;
        private OSGeo.FDO.Connections.ConnectionState ConState;

        public ArcSDEHelper(string _hostname, int _port, string _user, string _password, string _datastore)
        {
            this.hostname = _hostname;
			this.port = _port;
			this.user = _user;
			this.password = _password;
			this.datastore = _datastore;

			connStr = "Server=" + hostname + ";" + "Instance=" + port + ";" + "User=" + user + ";" + "Password=" + password + ";" + "Datastore=" + datastore + ";";
        }

        public IConnection connect()
		{
        	try
        	{
			    FDORegistry = FeatureAccessManager.GetProviderRegistry();
				FDOManager = FeatureAccessManager.GetConnectionManager();
				FDOConnection = FeatureAccessManager.GetConnectionManager().CreateConnection("OSGeo.ArcSDE.3.6"); // Replace the version of DLL your using…

				IConnectionPropertyDictionary connectionPropertyDictionary;
				connectionPropertyDictionary = FDOConnection.ConnectionInfo.ConnectionProperties;
				connectionPropertyDictionary.SetProperty("Server",hostname);
				connectionPropertyDictionary.SetProperty("Instance",port.ToString());
				connectionPropertyDictionary.SetProperty("Username",user);
				connectionPropertyDictionary.SetProperty("Password",password);
				connectionPropertyDictionary.SetProperty("Datastore",datastore);

//				FDO Gets the Datastore options out of a pending connection (2-step connection)
//				FDOConnection.Open();
//				string s = "";
//				string[] strArry = null;
//				IConnectionPropertyDictionary dict = FDOConnection.ConnectionInfo.ConnectionProperties;
//				foreach (string st in dict.PropertyNames) {
//
//					string val = dict.GetProperty(st);
//					string defVal = dict.GetPropertyDefault(st);
//					string localname = dict.GetLocalizedName(st);
//					bool isRequired = dict.IsPropertyRequired(st);
//					bool isEnumerable = dict.IsPropertyEnumerable(st);
//
//					if (isEnumerable)
//					{
//						strArry = new string[dict.EnumeratePropertyValues(st).Length];
//						dict.EnumeratePropertyValues(st).CopyTo(strArry,0);
//					}
//				}

				return FDOConnection;
        	}
        	catch (OSGeo.FDO.Common.Exception ex)
        	{
        		Console.WriteLine(ex.Message);
                Console.ReadLine();
                return null;
        	}
		}        

        public string ConnStr
        { get { return connStr; } }

        public DataTable ListSchema()
        {
            OSGeo.FDO.Connections.IConnection conn = connect();
            ConState = conn.Open();
            try
            {
            	if (null != conn && ConState == OSGeo.FDO.Connections.ConnectionState.ConnectionState_Open)
        		{
	            	// Get Schema Names:
					OSGeo.FDO.Commands.Schema.IGetSchemaNames pGSN =(OSGeo.FDO.Commands.Schema.IGetSchemaNames)conn.CreateCommand
						(OSGeo.FDO.Commands.CommandType.CommandType_GetSchemaNames);

					OSGeo.FDO.Common.StringCollection stCol = pGSN.Execute();

	            	dataTable = new DataTable();
	            	dataTable.Columns.Add("FeatureSchemaName", typeof(string));

					foreach ( OSGeo.FDO.Common.StringElement s in stCol)
					{
						dataTable.Rows.Add(s.String);
					}
					// sort dataTable
					DataView v = dataTable.DefaultView;
					v.Sort = "FeatureSchemaName ASC";
					dataTable = v.ToTable();

	                conn.Close();
	                return dataTable;
            	}
            	else {return null;}
            }
            catch (OSGeo.FDO.Common.Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
                return null;
            }
        }
        public DataTable ListTablesOfDB(string _schemaName)
        {
            OSGeo.FDO.Connections.IConnection conn = connect();
            ConState = conn.Open();
            try
            {
            	if (null != conn && ConState == OSGeo.FDO.Connections.ConnectionState.ConnectionState_Open)
        		{
	                //Get a list of tables in the DB
					OSGeo.FDO.Commands.Schema.IGetClassNames pGSN =(OSGeo.FDO.Commands.Schema.IGetClassNames)conn.CreateCommand
						(OSGeo.FDO.Commands.CommandType.CommandType_GetClassNames);

					OSGeo.FDO.Common.StringCollection stCol = pGSN.Execute();

	            	dataTable = new DataTable();
	            	dataTable.Columns.Add("FeatureClasses", typeof(string));

					foreach ( OSGeo.FDO.Common.StringElement s in stCol)
					{
						if (s.String.StartsWith(_schemaName))
						{
							string newStr = s.String.Replace(_schemaName + ':', string.Empty);
						}
					}

					// sort dataTable
					DataView v = dataTable.DefaultView;
					v.Sort = "FeatureClasses ASC";
					dataTable = v.ToTable();

	                conn.Close();
	                return dataTable;
            	}
            	else {return null;}
            }
            catch (OSGeo.FDO.Common.Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
                return null;
            }
            finally { conn.Close(); }
            }

        public string GetGeomColumn(string _tableName, string _schemaName)
        {
            string geomCol = "";
            OSGeo.FDO.Connections.IConnection conn = connect();
            ConState = conn.Open();
            try
            {
            	if (null != conn && ConState == OSGeo.FDO.Connections.ConnectionState.ConnectionState_Open)
            	{
					ISelect sel = (ISelect)conn.CreateCommand(OSGeo.FDO.Commands.CommandType.CommandType_Select);
					sel.SetFeatureClassName(_tableName);

					IFeatureReader FDOReader = sel.Execute();
					OSGeo.FDO.Schema.ClassDefinition cDef = FDOReader.GetClassDefinition();

					foreach (OSGeo.FDO.Schema.PropertyDefinition def in cDef.Properties)
					{
						if (def.PropertyType == OSGeo.FDO.Schema.PropertyType.PropertyType_GeometricProperty)
						{
							geomCol = def.Name;
						}
					}
	                conn.Close();
	                return geomCol;
            	}
            	else {return null;}
            }
            catch (OSGeo.FDO.Common.Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
                return null;
            }
            finally { conn.Close(); }

        }
		public FeatureSet GetAllFeatures(string _featureClassName, string _geomColumn)
        {
            OSGeo.FDO.Connections.IConnection conn = connect();
            ConState = conn.Open();

            if (ConState == OSGeo.FDO.Connections.ConnectionState.ConnectionState_Open
                && _featureClassName != null && (_geomColumn != null || _geomColumn != ""))
            {
                ISelect sel = (ISelect)conn.CreateCommand(OSGeo.FDO.Commands.CommandType.CommandType_Select);
                sel.SetFeatureClassName(_featureClassName);

                IFeatureReader FDOReader = sel.Execute();

                GeometryCollection Geo_Collection = new GeometryCollection();
                FgfGeometryFactory fdoGeoFac = new FgfGeometryFactory();

                OSGeo.FDO.Schema.ClassDefinition cDef = FDOReader.GetClassDefinition();

                DotSpatial.Data.FeatureSet fs = new FeatureSet();

                //Populate schema of DataTable
                foreach (OSGeo.FDO.Schema.PropertyDefinition pDef in cDef.Properties)
                {
                    // Only Data - no geometry!
                    if (OSGeo.FDO.Schema.PropertyType.PropertyType_DataProperty == pDef.PropertyType)
                    {
                        //Get Datatype
                        var dProDef = pDef as OSGeo.FDO.Schema.DataPropertyDefinition;
                        OSGeo.FDO.Schema.DataType typ = dProDef.DataType;
                        Type t = ChangeType(typ);
                        //Get Property name
                        string propertyName = pDef.Name;
                        fs.DataTable.Columns.Add(pDef.Name, t);
                    }
                }

                while (FDOReader.ReadNext())
                {
                    Feature feat = new Feature();
                    DataRow dr = fs.DataTable.NewRow();
                    foreach (PropertyDefinition pDef in cDef.Properties)
                    {
                        if (pDef.PropertyType == PropertyType.PropertyType_DataProperty)
                        {
                            DataPropertyDefinition dDef = pDef as DataPropertyDefinition;
                            switch (dDef.DataType)
                            {
                                case OSGeo.FDO.Schema.DataType.DataType_String:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetString(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_Int16:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetInt16(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_Int32:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetInt32(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_Int64:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetInt64(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_DateTime:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetDateTime(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_Single:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetSingle(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_Double:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetDouble(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_Decimal:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetDouble(dDef.Name);
                                    break;
                                case OSGeo.FDO.Schema.DataType.DataType_Boolean:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetBoolean(dDef.Name);
                                    break;
                                default:
                                    if (!FDOReader.IsNull(dDef.Name))
                                        dr[dDef.Name] = FDOReader.GetByte(dDef.Name);
                                    break;
                            }
                            feat.DataRow = dr;
                        }

                        if (pDef.PropertyType == PropertyType.PropertyType_GeometricProperty)
                        {
                            //Get Geometry with FDO Reader
                            Byte[] Tmppts = FDOReader.GetGeometry(_geomColumn);
                            IGeometry fdoGeo = fdoGeoFac.CreateGeometryFromFgf(Tmppts);
                            // Convert to WKB
                            Byte[] wkbFDO = fdoGeoFac.GetWkb(fdoGeo);

                            // Read WKB from FDO and convert to DotSpatial Geometry
                            DotSpatial.Topology.GeometryFactory geoFac = new DotSpatial.Topology.GeometryFactory();
                            DotSpatial.Topology.Utilities.WkbReader wkbReader = new DotSpatial.Topology.Utilities.WkbReader();
                            DotSpatial.Topology.IGeometry geom = wkbReader.Read(wkbFDO);

                            //Add DotSpatial Geometry
                            feat.BasicGeometry = geom;
                        }
                    }
                    fs.Features.Add(feat);
                }
                return fs;
            }
            else {return null;}
        }

        public static Type ChangeType(OSGeo.FDO.Schema.DataType dt)
		{

			switch (dt)
			{
				case OSGeo.FDO.Schema.DataType.DataType_BLOB:
				{
					return Type.GetType("Sytem.Object");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Boolean:
				{
					return Type.GetType("System.Boolean");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Byte:
				{
					return Type.GetType("System.Byte");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Int16:
				{
					return Type.GetType("System.Int16");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Int32:
				{
					return Type.GetType("System.Int32");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Int64:
				{
					return Type.GetType("System.Int64");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Single:
				{
					return Type.GetType("System.Single");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Double:
				{
					return Type.GetType("System.Double");
				}
				case OSGeo.FDO.Schema.DataType.DataType_Decimal:
				{
					return Type.GetType("System.Decimal");
				}
				case OSGeo.FDO.Schema.DataType.DataType_DateTime:
				{
					return Type.GetType("System.DateTime");
				}
				case OSGeo.FDO.Schema.DataType.DataType_String:
				{
					return Type.GetType("System.String");
				}
			}
		throw new ArgumentException("Unknown DataType");
		}
	}
}

5 Leute mögen diesen Artikel.

August 18th, 2010

ArcSDE – eine Multiuser-Geodatenbank? (4)

Fazit

Offensichtlich ist es wohl so, dass die Mechanismen einer SDE-Instanz auf der gesamten Datenbankebene verlaufen. Beispielsweise gibt es ein Datenbankschema SDE, in dem alle Verwaltungstabellen residieren.

Dabei müssten die Funktionalitäten doch auf Schemaebene getrennt werden. Es wäre super, wenn der Nutzer A ein eigenes Datenbankschema erhält und dort auch völlig getrennt von anderen Nutzern agieren könnte.

Ich habe versucht zwei verschiedene Schemas auf Datenbankebene anzulegen und auch mit den Rechten der Versionen (Private, Public und Protected) experimentiert – ohne Erfolg. Der Compress-Befehl wirkt sich einfach auf die gesamte Instanz aus und alle Add– und Delete-Tabellen werden im Schema SDE geführt nicht im jeweiligen Schema des Datenthemabesitzers.

In Sachdatenbanken kann man (nach meinem Kenntnisstand) völlig autark in seinem Schema Daten speichern, verändern, Prozeduren anlegen und Historisierungstabellen führen – warum nicht in einer mit ArcSDE erweiterten Datenbank?

Im Falle der ArcSDE lautet das Motto von ESRI leider wieder einmal: „Spatial is special“.

1 andere Person mag diesen Artikel.

August 18th, 2010

ArcSDE – eine Multiuser-Geodatenbank? (3)

Ein Szenario aus der Praxis sieht folgendermaßen aus. Die Koordinatoren A und B eines Datenthemas X haben die Anforderungen, dass Daten in der ArcSDE aus Dokumentationsgründen historisiert werden müssen. Zudem sollen die von mehreren Bearbeitern verändert werden können. Dabei werden mehrere Ebenen digitalisiert und in einigen Druchläufen geprüft und bearbeitet. Außerdem gibt es Daten, die sich aus den digitalisierten Daten ableiten lassen. Dies würde man in der Welt der Sachdatenbank mit einem View lösen. Auch in der ArcSDE gibt es hierzu die sogenannten Spatial Views. Diese leiten sich aus den vorhandenen Daten ab und werden auch in anderen Nicht-ESRI-Anwendungen verwendet.

weiterlesen »

Be the first to like.

August 18th, 2010

ArcSDE – eine Multiuser-Geodatenbank? (2)

Wie im letzten Artikel erwähnt kommt man bei fortgeschrittenen Anwendungen mit der ArcSDE nicht an der Versionierung vorbei.

Zum Thema Versionierung gibt es hier in der ESRI-Dokumentation mehr Informationen. Das Hauptmerkmal versionierter Daten ist, dass verschiedene Stände von einem Datensatz in der ArcSDE gehalten und abgeglichen werden können. Eigentlich eine sehr nützliche Funktion…

weiterlesen »

Be the first to like.

August 15th, 2010

ArcSDE – eine Multiuser-Geodatenbank? (1)

Ein Datenbanksystem ist unter normalen Umständen eine mehrbenutzerfähige Umgebung, die sehr vielen Benutzern die Möglichkeit bietet, Informationen zu lesen, zu verändern und auch neu zu erzeugen. So weit so gut.

Auch eine Geodatenbank, die mit Mechanismen ausgestattet ist, räumliche Informationen in geeigneten Speicherstrukturen abzulegen und effizient zu verwalten sollte diese Kerneigenschaft einer Datenbank eigentlich mitbringen. Mein Einblick in zwei wichtige Geodatenbanksysteme (PostgreSQL mit PostGIS und Oracle mit Oracle Spatial) bestätigte diese Eigenschaften bislang auch.

Bis vor einiger Zeit war mein Verständnis von Geodatenbanken im Lot, bis ich mich intensiver mit ArcSDE, der Middleware von ESRI beschäftigte…

weiterlesen »

Be the first to like.