ProConcepts Plugin Datasources - kataya/arcgis-pro-sdk GitHub Wiki
The Plugin Datasource framework allows developers to make custom datasources available for use in Pro. ArcGIS Pro treats these data sources as read-only tables or feature classes. Pro can use these data sources to execute queries, perform analysis, and display features on a map. This topic describes how to make this data available to ArcGIS Pro.
Language: C#
Subject: Plugin Datasources
Contributor: ArcGIS Pro SDK Team <[email protected]>
Organization: Esri, http://www.esri.com
Date: 11/24/2020
ArcGIS Pro: 2.7
Visual Studio: 2017, 2019
- Introduction to Plugin Datasources
- Understanding the Plugin Datasource Architecture
- Implementing a Plugin Datasource
- For More Information
ArcGIS Pro supports data from a large variety of data sources and formats. From shape files to Oracle databases, ArcGIS Pro can display and analyze data from many different formats. As extensive as the list is however, there are many other data formats that are not supported. This includes other relational databases such as MySQL, non-relational databases such as MongoDB, and a myriad of other proprietary or obscure file-based data stores.
The Plugin Datasource framework allows developers to make these and other data formats available for use in Pro. ArcGIS Pro treats these data sources as read-only tables or feature classes. Pro can execute queries, perform analysis, and display features on a map. This topic describes how to make this data available to ArcGIS Pro.
To understand how Plugin Datasources work, it first makes sense to understand how the basic high-level data access objects work in general.
- A Connector is used to define a connection to a data source. It is used to create a datastore
- A datastore represents the specific instance of a data source. It is used to open one or more tables or feature classes
- Queries can be executed against tables and feature classes, returning a cursor
- The user can then iterate through a cursor, returning rows
It helps to illustrate this with a specific example. When you use a client-server geodatabase such as Oracle, SQL Server, or Postgres, you use the following objects.
- Create a
DatabaseConnectionProperties
orDatabaseConnectionFile
object (both of which inherit from theConnector
base class) - Create a
Geodatabase
object using the connector.Geodatabase
inherits from theDatastore
base class - Open a
Table
orFeatureClass
using theOpenDataset()
method on the geodatabase - Use the
Search
orSelect
method on the table or feature class to get aRowCursor
- Iterate through the cursor using
MoveNext()
andCurrent
, which returns aRow
Plugin Datasources follow the exact same pattern. In this case, the framework provides a set of high-level objects.
- Create a
PluginDatasourceConnectionPath
object, which inherits from theConnector
base class - Create a
PluginDatastore
object using the connector.PluginDatastore
inherits from theDatastore
base class - Open a
Table
orFeatureClass
using theOpenTable()
method on the datastore. - Use the
Search
orSelect
method on the table or feature class to get aRowCursor
- Iterate through the cursor using
MoveNext()
andCurrent
, which returns aRow
The next section explores how a custom data source plugs into this framework.
The Plugin Datasource framework supports arbitrary data formats by allowing developers to implement core pieces of the datastore, table, and row cursor classes. Developers implement these core pieces by creating classes that inherit from PluginDatasourceTemplate, PluginTableTemplate, and PluginCursorTemplate, respectively. (The classes are named with the Template suffix because they implement the Template Method Design Pattern originally documented in the classic Design Patterns software engineering text.) The framework provides the general algorithms that allow Pro access to the data; the template classes allow custom code to fill in (or plug in) the details of the implementation.
These three user-created classes are described in the sections that follow. These sections describe the different classes and methods that must be implemented. For a step-by-step example of creating a plugin datasource, see the Pro Guide Plugin Datasources.
As mentioned above, there are three classes that must be created by the developer to implement a Plugin Datasource. The first of these is a class to implement a data store. This class must inherit from the PluginDatasourceTemplate
base class.
This plug-in datasource concrete class must implement the following public routines:
Method | Description |
---|---|
void Open(Uri connectionPath) |
This method should establish a connection to the underlying data source specified by connectionPath. For example, if this Plugin Datasource reads MySQL databases, this method would establish a connection to a MySQL database. |
bool CanOpen(Uri connectionPath) |
Some of the ArcGIS Pro subsystems may need to query this method to determine if a given data source can be opened by this data store. For example, if your Plugin Datastore exposes an Excel spreadsheet as a datasource, you should return true if the connectionPath URI represents an Excel file. |
void Close() |
This method closes the connection to the underlying data source that was established by the Open call. It should be used to release database connections, close file handles, and similar cleanup. |
IReadonlyList<string> GetTableNames() |
GetTableNames should return a list of all of the tables and feature classes that can be opened by the Plugin Datasource. Remember that the underlying data source might not be composed of database tables; these are entities that exposed to ArcGIS Pro as tables. For example, a Plugin Datasource that exposes an Excel spreadsheet as a datasource might treat each pane in the spreadsheet as an individual table. The format of the strings returned is up to the developer; the only stipulation is that the strings returned by GetTableNames can be used in calls to OpenTable . |
PluginTableTemplate OpenTable(string name) |
This routine conceptually opens a table, although again the backing datastore need not be a database table. The developer should create and return a plug-in table, which is a class that inherits from PluginTableTemplate . Conceptually, a .NET client calls the PluginDatastore.OpenTable() method which calls this routine, wraps up the returned PluginTableTemplate concrete instance as a ArcGIS.Core.Data.Table or ArcGIS.Core.Data.FeatureClass . |
The plug-in datasource concrete class may optionally implement the following additional public routines:
Method | Description |
---|---|
bool IsQueryLanguageSupported() |
This routine should return true if the underlying data source supports a query language. The default is false. See the Queries section of this document for more information about queries. |
string GetDatasourceDescription(bool inPluralForm) |
This routine is used to return a description of the datasources supported by this plug-in. For example, if your plug-in supports MongoDB databases, you might return “MongoDB database” and “MongoDB databases”, depending on the value of the inPluralForm parameter. If no implementation is provided, the default return values are “Plugin Datasources” and “Plugin Datasource” for inPluralForm equal to true and false respectively. |
string GetDatasetDescription(DatasetType datasetType) |
This routine is used to return a description of the datasets that are returned by this plug-in. Currently, the framework only supports DatasetType.Table and DatasetType.FeatureClass. If no implementation is provided, the default return values are “Table” and “Feature Class.” Remember that while ArcGIS Pro treats these datasets as if they were tables, the actual implementation may not correspond to a database table. This routine allows ArcGIS Pro to use different, more accurate, terminology. |
The ArcGIS.Core.Data.PluginDatastore.PluginDatasourceConnectionPath
class is a connector class that is used to open a plugin datasource. It is created with two parameters- a plugin identifier, and a datasource path.
The plugin identifier is a string ID. This must match the plugin data source ID that is included with your plugin datasource within its Config.xml file (typically this is generated by the Visual Studio template). This parameter tells the framework which plugin datasource class works with this connector.
The datasource path parameter is a Uri. It can be a physical or logical path (that has meaning to your concrete plugin datasource class).
When the PluginDatastore
constructor is called, the framework instantiates the concrete class specified by the plugin identifier. It then calls Open()
on that class, passing in the datasource path parameter.
Another class that must be implemented is a plug-in table. This class is used for both tables and feature classes and must inherit from the ArcGIS.Core.Data.PluginDatastore.PluginTableTemplate
base class.
The plug-in table concrete class must implement the following public routines:
Method | Description |
---|---|
string GetName() |
Returns the name of the table. This string should be one of the names that is returned by GetTableNames() on the plug-in datasource concrete class. |
IReadOnlyList<PluginField> GetFields() |
Returns the set of fields that are available on this table. The framework will take the list of PluginField objects that are returned, wrap them up, and return them as ArcGIS.Core.Data.Field objects from the appropriate routines on TableDefinition . If the list of PluginField objects contains a field whose type is FieldType.Geometry than this plug-in table is considered a feature class. If no field of this type is returned, the plug-in table is treated as a non-spatial table by ArcGIS Pro. |
PluginCursorTemplate Search(QueryFilter queryFilter) |
This routine should perform a non-spatial query. More information about queries is included below. |
PluginCursorTemplate Search(SpatialQueryFilter queryFilter) |
This routine should perform a spatial query. If this table is a non-spatial table rather than a feature class, this routine should throw an exception. |
The plug-in datasource concrete class may optionally implement the following additional public routines:
Method | Description |
---|---|
bool IsNativeRowCountSupported() |
Returns whether or not the table or feature class can natively return a row count. If true is returned, and a row count is needed, the framework will call the GetNativeRowCount() routine. If false is returned (the default), and a row count is needed, the framework will call Search() on the entire table and manually iterate through the cursor to determine the number of rows. For performance reasons, if the underlying datastore supports a faster row count technique, this routine should return true. |
int GetNativeRowCount() |
If IsNativeRowCountSupported() returns true, this routine is called by the framework to obtain a row count. |
GeometryType GetShapeType() |
The type of shape this plug-in table supports. The default, which should also be returned for all non-spatial tables, is GeometryType.Unknown . |
As noted above, returning a field of type FieldType.Geometry
is all that is necessary to mark this table as a feature class.
The implementation of the PluginTable.Search()
methods should return a concrete class that inherits from PluginCursorTemplate
, as described in the next section. The framework will wrap up this PluginCursorTemplate
object and return it to the user as part of a RowCursor
object.
The behavior of queries with the Search()
method is dependent in a large part to the value returned by PluginDatasourceTemplate.IsQueryLanguageSupported()
.
If this routine returns true, the framework will take the where clause that the user passed into Table.Search()
and pass it directly to the PluginTable.Search()
routine. This is the obvious implementation if the underlying data store (e.g., MySQL) supports a query language. It’s also possible for developers to write their own query parser if the plugin datasource doesn’t support queries (e.g, a text file).
If the routine returns false, the framework will not relay the user-provided where clause to the PluginTable.Search()
routine. In this case, the framework will attempt to filter out these rows itself. For example, if the where clause reads StateName = ‘Colorado’
, the framework will read through the cursor returned by the PluginTable and filter out all of those rows where the StateName field does not equal “Colorado”. Obviously, executing this filter in the underlying data store, if it is supported, should be preferred for better performance.
Finally, if the user-provided where clause contains subexpressions involving a field of type FieldType.OID
the framework will fill in the QueryFilter.ObjectIDs
property and pass it into PluginTable.Search()
. If the underlying plug-in table doesn’t provide an ObjectID field, it is the implementor’s responsibility to handle this. For example, the plug-in table might decide to treat the first row as having an ObjectID of 1, the second row as having an ObjectID of 2, and so on.
The final class that must be implemented for a plug-in datasource is a plug-in cursor. This class must inherit from the ArcGIS.Core.Data.PluginDatastore.PluginCursorTemplate
base class.
The plug-in cursor concrete class must implement the following public routines:
Method | Description |
---|---|
PluginRow GetCurrentRow() |
This method should return the current row that the cursor points to. This should be done by creating and returning a PluginRow object. If the cursor has advanced past the end of the table (i.e., MoveNext() returned false), this routine should return a null. The PluginRow object that is returned here is wrapped up by the framework and exposed to the user as a Row . |
bool MoveNext() |
Should return true if the cursor has successfully advanced to the next row; otherwise, false. If MoveNext() returns false, the framework will not call GetCurrentRow() . |
The ArcGIS.Core.Data.PluginDatastore.PluginRow
class is a simple class that contains a Values
property. This property contains a list of objects that represent the values of the row. The number of values in the list and the type of each value must match those returned by PluginTableTemplate.GetFields()
.
For more information about how to implement plugin datasources, see the Pro Guide Plugin Datasources.
Consult Pro Plugin Registry Keys within the ArcGIS Pro Registry Keys document.