[mapguide-commits] r10068 - in branches/4.0/MgDev/Doc/landing: . topics

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Fri Nov 10 12:21:37 PST 2023


Author: jng
Date: 2023-11-10 12:21:36 -0800 (Fri, 10 Nov 2023)
New Revision: 10068

Added:
   branches/4.0/MgDev/Doc/landing/topics/feature-connection.md.tpl
   branches/4.0/MgDev/Doc/landing/topics/feature-filters.md.tpl
   branches/4.0/MgDev/Doc/landing/topics/feature-intro.md.tpl
   branches/4.0/MgDev/Doc/landing/topics/feature-provider-registry.md.tpl
   branches/4.0/MgDev/Doc/landing/topics/feature-provider.md.tpl
   branches/4.0/MgDev/Doc/landing/topics/feature-schema.md.tpl
Modified:
   branches/4.0/MgDev/Doc/landing/toc.yml
   branches/4.0/MgDev/Doc/landing/topics/toc.yml
Log:
#2877: 
 - Finish migrating across feature service conceptual topics
 - Add .net/Java/PHP top-level links

Modified: branches/4.0/MgDev/Doc/landing/toc.yml
===================================================================
--- branches/4.0/MgDev/Doc/landing/toc.yml	2023-11-10 19:15:59 UTC (rev 10067)
+++ branches/4.0/MgDev/Doc/landing/toc.yml	2023-11-10 20:21:36 UTC (rev 10068)
@@ -1,3 +1,9 @@
 # TODO: Link to conceptual topics once they've all been migrated to markdown and copied into this dir
 - name: Conceptual Topics
-  href: topics/toc.yml
\ No newline at end of file
+  href: topics/toc.yml
+- name: .net Reference
+  href: dotnet_api/index.html
+- name: Java Reference
+  href: java_api/index.html
+- name: PHP Reference
+  href: php_api/index.html
\ No newline at end of file

Added: branches/4.0/MgDev/Doc/landing/topics/feature-connection.md.tpl
===================================================================
--- branches/4.0/MgDev/Doc/landing/topics/feature-connection.md.tpl	                        (rev 0)
+++ branches/4.0/MgDev/Doc/landing/topics/feature-connection.md.tpl	2023-11-10 20:21:36 UTC (rev 10068)
@@ -0,0 +1,542 @@
+# Connection to Feature Source
+
+## About feature sources
+
+A feature source contains feature data. The term 'feature
+source' refers to a feature data resource in the site server
+repository. The feature source is installed in the repository
+using the resource service (`MgResourceService`).
+
+What constitutes a feature source varies depending on the
+nature of the actual data source. For example, some data
+sources are text files, some are binary files, and some are
+relational databases. If the data source is a file, the file
+itself is stored in the repository. If the data source is a
+relational database, a connection specification is stored in
+the repository. Some relational databases support
+partitioning into multiple data sources. Each partition
+requires a separate connection specification.
+
+## Storing and identifying feature sources
+
+In addition to features sources, the site server repository
+contains many other objects, which are collectively referred
+to as resources and identified by resource identifiers
+(`MgResourceIdentifier`) [!doclinkclass MgResourceIdentifier]. The resource identifier specifies the
+location of a resource in the repository by using a URL.
+
+The feature source URL is of the form
+`Library:://.../<featureSourceName>.FeatureSource`. The
+ellipsis (`...`) represents an optional folder structure
+contained within the root Library folder. An example
+identifier is
+`Library://FeatureService/PointsMDB.FeatureSource`. The
+FeatureSource extension is specific to the feature source
+resource. All of the methods in the MgFeatureService API that
+do actual work require an MgResourceIdentifier object as an
+argument.
+
+<p>
+  
+</p>
+
+## Before installing a feature source in the repository
+
+The `MgFeatureService` [!doclinkclass MgFeatureService] API provides a `TestConnection`
+method for testing the connection parameter values for a
+feature source prior to installation in the repository. For
+file-based feature sources the connection value could be a
+file name, a folder name, or a symbolic name which is used
+by the provider to query the operating system for the file
+location. For relational database feature sources, possible
+connection values include host name, service name, service
+instance name, user name, password, and datastore name.
+
+The connection string used to connect to a feature source is
+specific to the feature source. Examples follow for various
+providers.
+
+The `MgFeatureService` API provides a `GetConnectionPropertyValues`
+method for listing the names of the feature source partitions
+within a relational database. You can then construct a
+connection string for each partition and test it using `TestConnection`
+
+After the installation of the feature source in the
+repository, connection is done by a different `TestConnection(MgResourceIdentifier)`
+method that takes the feature source's resource identifier as
+an argument.
+
+## Installing the feature source in the repository
+
+Install a feature source in the repository by:
+ * Specifying the location of the feature source in the
+repository in a resource identifier.
+ * Uploading a feature source XML value containing the
+property values used to connect to and configure the feature
+source. The structure of a feature source XML value conforms
+to the [FeatureSource](xmlschema-featuresource.md) XML schema. The element values
+vary according to the feature source type. Examples for each
+provider follow.
+ * Uploading an XML value containing values governing the
+security of the feature source. See the \link ResourceDocumentHeader_schema ResourceDocumentHeader \endlink
+and [ResourceSecurity](xmlschema-resourcesecurity.md) schema topics for a general
+discussion about the XML element definitions. See the
+contents of an example header file at the end of this topic.
+ * Uploading the file or files which constitute the feature
+source if the feature source is file-based.
+Upload the feature source property XML file and document
+header XML file using the 
+`MgResourceService::SetResource` method
+Upload a feature source file using the `MgResourceService::SetResourceData` method.
+
+## Examples for various providers
+
+### Autodesk.Oracle.2.0 Provider
+
+<p>
+  The feature source is a relational database, which supports
+  multiple partitions, referred to as datastores. Each
+  datastore can be a feature source. You use Sysadmin.exe to
+  create datastores and add usernames and passwords. The tool
+  and documentation for doing these and other tasks is
+  contained in the installation. For a typical installation,
+  the path is <i>C:\\Program Files\\Common Files\\Autodesk
+  Shared\\GIS\\FDO\\2.0\\Oracle</i>.
+</p>
+
+Example Connection String
+
+```
+Username=smith;Password=test;Service=TuxService;DataStore=TUX
+```
+
+All parameters are required.
+
+The `DataStore` property value identifies the feature source
+partition within the database. This value can be retrieved
+from the database using the `MgFeatureService::GetConnectionPropertyValues` Method.
+
+The Service property value identifies an entry in the `tnsnames.ora` file located in the Oracle instance or Oracle client
+installation on the local host.
+
+Feature Source XML File
+
+The filename is `TuxService.FeatureSource`.
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<FeatureSource version="1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+  <Provider>Autodesk.Oracle.2.0</Provider>
+  <ConnectionProperties>
+    <ConnectionProperty>
+      <Name>DataStore</Name>
+      <Value>TUX</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Username</Name>
+      <Value>smith</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Password</Name>
+      <Value>test</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Service</Name>
+      <Value>TuxService</Value>
+    </ConnectionProperty>
+  </ConnectionProperties>
+  <Configuration/>
+  <SpatialContext/>
+  <LongTransaction/>
+</FeatureSource>
+```
+
+### Autodesk.ArcSDE.1.0 Provider
+
+The feature source is a relational database. Access to it is
+indirect. The provider talks to an ArcSDE client, which talks
+to the ArcSDE server, which talks to the feature source.
+
+Example Connection String
+
+```
+Username=smith;Password=test;Server=otwhost1;Instance=sde_inst1;Datastore=Default Datastore
+```
+
+All parameters are required.
+
+The `Server` value is a hostname whose IP address can be
+resolved using a network domain name server.
+
+The `instance` value must appear in the services file on the
+local host. The path is `C:\\WINDOWS\\system32\\drivers\\etc\\services`.
+An example entry is `sde_inst1 5151/tcp \#ArcSDE Server listening port`.
+
+The `Datastore` property value identifies the feature source
+partition within the database. This value can be retrieved
+from the database using the `MgFeatureService::GetConnectionPropertyValues` Method.
+
+A value of `Default Datastore` indicates that only one
+partition exists.
+
+Feature Source XML File
+
+The filename is `sde_inst1.FeatureSource`
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<FeatureSource version="1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+  <Provider>Autodesk.ArcSDE.1.0</Provider>
+  <ConnectionProperties>
+    <ConnectionProperty>
+      <Name>Datastore</Name>
+      <Value>Default Datastore</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Username</Name>
+      <Value>smith</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Password</Name>
+      <Value>test</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Server</Name>
+      <Value>otwhost1</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Instance</Name>
+      <Value>sde_inst1</Value>
+    </ConnectionProperty>
+  </ConnectionProperties>
+  <Configuration/>
+  <SpatialContext/>
+<LongTransaction/>
+</FeatureSource>
+```
+
+### Autodesk.Sdf.3.0 Provider
+
+The feature source is a file. As part of installing the
+feature source in the repository, the <i>.sdf</i> file is
+loaded into the repository.
+
+Example Connection String
+
+```
+File=C:/SDFFeatureResources/testSDF.sdf;ReadOnly=FALSE
+```
+
+The `File` parameter is mandatory, and the `ReadOnly` parameter
+defaults to `FALSE`.
+
+Feature Source XML File
+
+The `ConnectionProperty` element whose `Name` is `File` has a `Value` element that is a symbolic reference to the location of the `.sdf` file in the repository.
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<FeatureSource version="1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+  <Provider>Autodesk.Sdf.3.0</Provider>
+  <ConnectionProperties>
+    <ConnectionProperty>
+      <Name>File</Name>
+      <Value>%MG_DATA_FILE_PATH%</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>ReadOnly</Name>
+      <Value>FALSE</Value>
+    </ConnectionProperty>
+  </ConnectionProperties>
+  <SpatialContext/>
+  <LongTransaction/>
+</FeatureSource>
+```
+
+### Autodesk.Shp.1.0 Provider
+
+The feature source is a set of up to 4 related files,
+collectively referred to as shapefiles. They consist of a <i>.shp</i> file containing the shape geometry, the <i>.shx</i> file
+containing the shape row index, the <i>.dbf</i> file
+containing shape attributes in dBASE format, and the <i>.prj</i> file containing the coordinate system.
+
+It is possible to connect to a <i>.shp</i> file by itself.
+The SHP provider treats each <i>.shp</i> and associated <i>.dbf</i> file as a feature class with a single geometry property and
+optionally, with data attribute properties.
+
+The four files can be accessed by applications other than the
+provider. In addition the provider may create two files that
+are used by the provider and not by other applications: an <i>.idx</i> file containing a spatial index and an .xml configuration
+file containing a mapping of the SHP data in the <i>.shp</i> file and the DBF data in the <i>.dbf</i> file to feature
+classes and properties in the FDO data model.
+
+The connection string used as an argument to the
+`TestConnection()` method references a folder in the file
+system. As part of installing the feature source in the
+repository, the shapefiles (<i>.shp</i>, <i>.shx</i>, <i>.dbf</i>,
+and <i>.prj</i> files) are loaded into the repository. The
+value of the DefaultFileLocation element as specified in the
+feature source XML file is a symbolic reference to the
+location of the shapefiles in the repository.
+
+Example Connection String
+
+```
+DefaultFileLocation=C:/SHPFeatureResources;TemporaryFileLocation=C:/SHPFeatureResources
+```
+
+Feature Source XML File
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<FeatureSource version="1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+  <Provider>Autodesk.Shp.1.0</Provider>
+  <ConnectionProperties>
+    <ConnectionProperty>
+      <Name>DefaultFileLocation</Name>
+      <Value>%MG_DATA_FILE_PATH%</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>TemporaryFileLocation</Name>
+    </ConnectionProperty>
+  </ConnectionProperties>
+  <SpatialContext/>
+  <LongTransaction/>
+</FeatureSource>
+```
+
+### Autodesk.RFP.1.0 Provider
+
+Example Connection String
+
+```
+DefaultRasterFileLocation=C:/RasterFeatureResources
+```
+
+Feature Source XML File
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<FeatureSource version="1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+  <Provider>Autodesk.RFP.1.0</Provider>
+  <ConnectionProperties>
+    <ConnectionProperty>
+      <Name>DefaultRasterFileLocation</Name>
+      <Value>%MG_DATA_FILE_PATH%</Value>
+    </ConnectionProperty>
+  </ConnectionProperties>
+  <SpatialContext/>
+  <LongTransaction/>
+</FeatureSource>
+```
+
+### Autodesk.ODBC.1.0 Provider
+
+Typically you use this provider is to access text-based
+feature source files.
+
+The essential first step is to use a Windows OS
+administrative tool to define a data source name. This action
+associates a symbolic name with a file path. You then use the
+symbolic name in the connection string and XML feature source
+file.
+
+On an XP computer, the path to the ODBC Data Source
+Administrator is Start > Settings > Control Panel >
+Administrative Tools > Data Sources (ODBC). You can create a
+User DSN (Data Source Name), a System DSN or File DSN in the
+ODBC Data Source Administrator dialog box.
+
+The sequence of clicks or data entry to create a User or
+System DSN for a Microsoft Access <i>.mdb</i> file is System
+DSN or User DSN/Add > Create New Data Source/Microsoft
+Access Driver (<i>*.mdb</i>) > Finish > ODBC Microsoft
+Access Setup/<type name> > Select > Select
+Database/<select folder> > <select file> > OK > ODBC
+Microsoft Access Setup/OK. The name that you enter is the DSN
+name and is used in the connection string and the XML feature
+source file.
+
+The sequence of clicks or data entry to create a User or
+System DSN for a dBase <i>.dbf</i> file is System DNS or User
+DSN/Add > Create New Data Source/Microsoft dBase Driver (<i>*.dbf</i>)
+> Finish > ODBC dBase Setup/<type name> > Save > Create
+New Data Source/Next > Finish > Select Directory> Select
+Database/<select folder> > <select file> > OK > ODBC
+dBase Setup/OK. If "Select Directory" is unavailable, clear
+"Use Current Directory". The name that you enter is the DSN
+name and is used in the connection string and XML feature
+source file.
+
+The sequence of clicks or data entry to create a File DSN for
+a Microsoft Access <i>.mdb</i> file is File DSN/Add > Create
+New Data Source/Microsoft Access Driver (<i>*.mdb</i>) >
+Next> Browse > Save As/<pick folder> > <type filename>
+Finish > ODBC Microsoft Access Setup/Select > Select
+Database/<select folder> > <select file> > OK > ODBC
+Microsoft Access Setup/OK. The filename that you enter will
+be given an extension of <i>.dsn</i>.
+
+Example Connection String
+
+```
+DataSourceName=Country
+```
+
+Feature Source XML File
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<FeatureSource version="1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="FeatureSource-1.0.0.xsd">
+  <Provider>Autodesk.ODBC.1.0</Provider>
+  <ConnectionProperties>
+    <ConnectionProperty>
+      <Name>DataSourceName</Name>
+      <Value>Country</Value>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>UserId</Name>
+    </ConnectionProperty>
+    <ConnectionProperty>
+      <Name>Password</Name>
+    </ConnectionProperty>
+  </ConnectionProperties>
+  <Configuration/>
+  <SpatialContext/>
+  <LongTransaction/>
+</FeatureSource>
+```
+
+ResourceDocumentHeader XML File
+
+```XML
+<?xml version="1.0" encoding="UTF-8"?>
+<ResourceDocumentHeader xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="ResourceDocumentHeader-1.0.0.xsd">
+  <Security>
+    <Inherited>true</Inherited>
+  </Security>
+</ResourceDocumentHeader>
+```
+
+## Examples
+
+The code samples here how to add feature sources to the
+repository. You connect to a feature source in the
+repository. For example code showing how to connect to a
+feature source, see `MgFeatureService::TestConnection`.
+
+The first example adds a resource to the repository for a
+connection to an FDO Provider for Oracle. Since the
+connection is to a relational database and not a file, you
+need only call `MgResourceService::SetResource` passing in the
+FeatureSource XML containing the property values for the
+connection to the provider.
+
+PHP code
+
+```PHP
+<?php
+// creating the resource in the repository for a
+// connection to an FDO Provider for Oracle
+// first arg to $resourceService->SetResource()
+$featSrcResIdArg = "Library://FeatureService/testOracle.FeatureSource";
+$featSrcResId = new MgResourceIdentifier($featSrcResIdArg);
+
+// second arg to $resourceService->SetResource()
+$featSrcFileByteContent = new MgByteSource($pathToFeatSrcFile);
+$featSrcFileReaderContent = $featSrcFileByteContent->GetReader();
+
+// third arg to $resourceService->SetResource()
+$headerPathByteContent = new MgByteSource($headerPath);
+$headerPathReaderContent = $headerPathByteContent->GetReader();
+
+// add the resource to the repository
+$resourceService->SetResource($featSrcResId, $featSrcFileReaderContent, $headerPathReaderContent);
+?>
+\endcode
+\htmlinclude ExampleBottom.html
+\htmlinclude PHPExampleTop.html
+The second example adds a resource to the repository for a
+connection to an FDO Provider for SDF. Since the connection
+is to a file, you must call MgResourceService::SetResource
+passing in the FeatureSource XML containing the property
+values for the connection to the SDF file, and you must call
+MgResourceService::SetResourceData passing in the contents of
+the SDF file.
+\code
+<?php
+// creating the resource in the repository for a
+// connection to an FDO Provider for SDF
+// first arg to $resourceService->SetResource()
+$featSrcResIdArg = "Library://FeatureService/testSDF.FeatureSource";
+$featSrcResId = new MgResourceIdentifier($featSrcResIdArg);
+
+// second arg to $resourceService->SetResource()
+$featSrcFileByteContent = new MgByteSource($pathToFeatSrcFile);
+$featSrcFileReaderContent = $featSrcFileByteContent->GetReader();
+
+// third arg to $resourceService->SetResource()
+$headerPathByteContent = new MgByteSource($headerPath);
+$headerPathReaderContent = $headerPathByteContent->GetReader();
+
+// add the resource to the repository
+$resourceService->SetResource($featSrcResId, $featSrcFileReaderContent, $headerPathReaderContent);
+
+// add the contents of the SDF file to the repository
+$fileByteContent = new MgByteSource($pathToDataFile);
+$fileReader = $fileByteContent->GetReader();
+$resourceService->SetResourceData($featSrcResId, $fileName, "File", $fileReader);
+?>
+```
+
+C# code
+
+```C#
+using OSGeo.MapGuide;
+using OSGeo.MapGuide.Schema.FeatureSource;
+// The MgResourceService example code shows the creation of an instance.
+private MgResourceService resourceService;
+
+public void ConnectToSdfFeatureClassFile(MgResourceIdentifier resourceId,
+	Boolean readOnly, String filename, String SDFProviderName)
+{
+	FileInfo info = new FileInfo(fileName);
+
+	// Check if the specified file exists
+	if (!info.Exists)
+	{
+		throw new FileNotFoundException(
+			string.Format("The specified file {1} doesn't exist.", info.FullName));
+	}
+
+	// an xml string containing values for the two SDF connection parameters: File and ReadOnly
+	String featureSourceDefinition;
+
+	// Build the feature source object model
+	FeatureSourceType fsType = new FeatureSourceType();
+
+	fsType.Provider = SDFProviderName; // FDO provider name, case sensitive
+
+	NameValuePairType p1 = new NameValuePairType();
+	p1.Name = "ReadOnly";
+	p1.Value = readOnly.ToString(); // non case sensitive
+	NameValuePairType p2 = new NameValuePairType();
+	p2.Name = "File";
+	p2.Value = info.FullName; // Either double backslash or single backslash is OK for the file path
+
+	fsType.Parameter = new NameValuePairType[] { p2, p1 };
+
+	// Serialize the feature source object model to xml string
+	using (StringWriter writer = new StringWriter())
+	{
+		XmlSerializer xs = new XmlSerializer(fsType.GetType());
+		xs.Serialize(writer, fsType);
+		featureSourceDefinition =  writer.ToString();
+	}
+
+	// Add the resource to repository
+	byte[] bytes = Utilities.StringToBytes(featureSourceDefinition);
+	MgByteSource source = new MgByteSource(bytes, bytes.Length);
+	resourceService.SetResource(resourceId, source.GetReader(), null);
+}
+```
\ No newline at end of file

Added: branches/4.0/MgDev/Doc/landing/topics/feature-filters.md.tpl
===================================================================
--- branches/4.0/MgDev/Doc/landing/topics/feature-filters.md.tpl	                        (rev 0)
+++ branches/4.0/MgDev/Doc/landing/topics/feature-filters.md.tpl	2023-11-10 20:21:36 UTC (rev 10068)
@@ -0,0 +1,794 @@
+# Filters and Expressions
+
+Use a filter to select a subset of the features in a
+datastore. Filters are used in calls to
+`MgFeatureService::SelectFeatures()` and
+`MgFeatureService::SelectAggregate()`. There are two types of
+filters: basic and spatial.
+
+## Spatial Filter 
+
+A spatial filter relates two geometries by way of a spatial
+operator. A spatial filter is set by a call to
+`MgFeatureQueryOptions::SetSpatialFilter()` or
+`MgFeatureAggregateOptions::SetSpatialFilter()`. These methods
+take 3 arguments: a name which identifies a geometry property
+of a feature in a datastore, a geometry object, and a
+spatial operation identifier. The effect of the filter is to
+select features from the datastore whose geometry property is
+related according to the spatial operator to the geometry
+object argument. For example, if the spatial operator is
+MgFeatureSpatialOperations::Within, and the geometry object's
+WKT representation is `POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))`,
+then the effect of the filter is to select those features
+which have geometries within this polygon.
+
+## Basic Filter
+
+ Use non-geometry feature property names, data values, and
+operators to construct a filter string which is passed as an
+argument to the `MgFeatureQueryOption::SetFilter()` or
+`MgFeatureAggregateOptions::SetFilter()` method. For example,
+if FEATID is a feature property name, then the filter "FEATID > 20" selects the features whose FEATID has a value greater
+than 20.
+
+## Expressions
+
+Expressions are subcomponents of a basic filter. One
+expression might constitute the entire filter, or several
+expressions can be strung together using operators.
+
+## Basic Filter Grammar
+
+```
+<Filter> ::= '(' Filter ')'
+| <LogicalOperator>
+| <SearchCondition>
+<LogicalOperator> ::= <BinaryLogicalOperator>
+| <UnaryLogicalOperator>
+
+<BinaryLogicalOperator> ::=
+
+<Filter> <BinaryLogicalOperations> <Filter>
+
+<SearchCondition> ::= <InCondition>
+| <ComparisonCondition>
+| <GeometricCondition>
+| <NullCondition>
+
+<InCondition> ::= <Identifier> IN '('
+ValueExpressionCollection ')'
+
+<ValueExpressionCollection> ::= <ValueExpression>
+| <ValueExpressionCollection> ',' <ValueExpression>
+
+<ComparisonCondition> ::=
+
+<Expression> <ComparisonOperations> <Expression>
+
+<GeometricCondition> ::= <SpatialCondition> |
+<DistanceCondition>
+
+<NullCondition> ::= <Identifier> NULL
+
+<SpatialCondition> ::= <Identifier> <SpatialOperations>
+<Expression>
+
+<DistanceCondition> ::= <Identifier> <DistanceOperations>
+<Expression> <distance>
+
+<UnaryLogicalOperator> ::= NOT <Filter>
+
+<BinaryLogicalOperations> ::= AND | OR
+
+<ComparisionOperations> ::= = 
+| <>
+| >
+| >=
+| <
+| <=
+| LIKE
+
+<SpatialOperations> ::= CONTAINS | CROSSES | DISJOINT |
+EQUALS | INTERSECTS | OVERLAPS | TOUCHES | WITHIN | COVEREDBY
+| INSIDE
+
+<DistanceOperations> ::= BEYOND | WITHINDISTANCE
+
+<distance> ::= BEYOND | WITHINDISTANCE
+```
+
+## Expression Grammar
+
+```
+<Expression> ::= '(' Expression ')'
+| <UnaryExpression>
+| <BinaryExpression>
+| <Function>
+| <Identifier>
+| <ValueExpression>
+
+<BinaryExpression> ::= <Expression> '+' <Expression>
+| <Expression> '-' <Expression>
+| <Expression> '*' <Expression>
+| <Expression> '/' <Expression>
+
+<ValueExpression> ::= <LiteralValue>
+
+<LiteralValue> ::= <GeometryValue> | <DataValue>
+
+<GeometryValue> ::= GEOMFROMTEXT '(' STRING ')'
+
+<DataValue> ::= TRUE
+| FALSE
+| DATETIME
+| DOUBLE
+| INTEGER
+| STRING
+| BLOB
+| CLOB
+| NULL
+
+<Function> ::= <Identifier> '(' <ExpressionCollection>
+')'
+
+<ExpressionCollection> ::=
+| <Expression>
+| <ExpressionCollection> ',' <Expression>
+
+<Identifier> ::= IDENTIFIER
+
+<UnaryExpression> ::= '-' <Expression>
+```
+
+## Operator Precedence
+
+The operator precedence from highest to lowest is:
+
+Negate NOT
+Multiply Divide
+Add Subtract
+= <> > >= < <=
+AND
+OR
+
+## Filter and Expression Keywords
+
+The following case-insensitive keywords are reserved in the
+language. That is, they cannot be used as identifier or
+function names:
+
+```
+AND BEYOND COMPARE CONTAINS COVEREDBY CROSSES DATA DISJOINT
+DISTANCE EQUALS FALSE GEOMFROMTEXT IN INSIDE INTERSECTS LIKE
+NOT NULL OR OVERLAPS RELATE SPATIAL TIME TIMESTAMP TOUCHES
+TRUE WITHIN WITHINDISTANCE
+```
+
+## STRING
+
+Strings are literal constants enclosed in single quotes. If
+you need to include a single quote character inside a string,
+you can double the character, for example, 'aaa''bbb'.
+
+## IDENTIFIER
+
+An identifier can be any alphanumeric sequence of characters
+other than a keyword. Identifiers can be enclosed in double
+quotes to show special characters and white space. If you
+need to include a double quote character inside an
+identifier, you can double the character, for example
+"abc""def".
+
+## INTEGER
+Integers allow only decimal characters with an optional unary
+minus sign. Unary plus is not supported. If an integer is out
+of the 32-bit precision range, it is converted to floating
+point.
+
+## DOUBLE
+Floating point numbers have a decimal point, can be signed
+(-), and include an optional exponent (e{[0-9]}).
+
+## DATETIME
+
+Date and time are parsed using the standard literal strings:
+
+`DATE 'YYYY-MM-DD'`
+`TIME 'HH:MM:SS[.sss]'`
+`TIMESTAMP 'YYYY-MM-DD HH:MM:SS[.sss]'`
+
+## CLOB/BLOB
+
+These data types are not currently supported. If you need to
+support binary input, use parameters.
+
+## `<Function>`
+
+Some functions such as ceil, floor, concat, lower, and upper
+take as an argument the value of a property from a single
+feature and return a value related to the property value.
+
+Some functions such as avg, count, max, min, stddev, and sum
+take as an argument the values of a property from multiple
+features and return a single value related to the values of
+the group of property arguments.
+
+<TABLE class="RuledTable">
+  <tr>
+    <th>Function</th>
+    <th>Description</th>
+  </tr>
+  <tr>
+    <td>Double Avg(n)</td>
+    <td>Average value of n, ignoring nulls</td>
+  </tr>
+  <tr>
+    <td>Int64 Ceil(Double)</td>
+    <td>Smallest integer >= Double</td>
+  </tr>
+  <tr>
+    <td>String Concat(Str1, Str2)</td>
+    <td>Concatenates Str1 and Str2</td>
+  </tr>
+  <tr>
+    <td>Int64 Count(expression)</td>
+    <td>Number of features where expression is not null</td>
+  </tr>
+  <tr>
+    <td>Int64 Floor(Double)</td>
+    <td>Largest integer <= Double</td>
+  </tr>
+  <tr>
+    <td>String Lower(Str)</td>
+    <td>Str with all lowercase letters</td>
+  </tr>
+  <tr>
+    <td>Double Min(expression)</td>
+    <td>Minimum value of expression</td>
+  </tr>
+  <tr>
+    <td>Double Max(expression)</td>
+    <td>Maximum value of expression</td>
+  </tr>
+  <tr>
+    <td>Double Stddev(n)</td>
+    <td>Standard deviation of n, ignoring nulls</td>
+  </tr>
+  <tr>
+    <td>Double Sum(n)</td>
+    <td>Sum of values of n</td>
+  </tr>
+  <tr>
+    <td>String Upper(Str)</td>
+    <td>Str with all uppercase letters</td>
+  </tr>
+</TABLE>
+
+## Examples
+
+PHP and C# sample code for the setting of filters for select operations is
+presented. The SQL expression equivalent to the PHP and C# code is also
+presented.
+
+PHP Code
+
+```PHP
+<?php
+$queryOptions = new MgFeatureQueryOptions();
+$stringCollection = new MgStringCollection();
+$wktReaderWriter = new MgWktReaderWriter();
+?>
+```
+
+C# code
+
+```C#
+using OSGeo.MapGuide;
+private MgFeatureQueryOptions queryOptions;
+private MgStringCollection stringCollection;
+private MgWktReaderWriter wktReaderWriter;
+private String featClassName = "SdfFeatureClass";
+private MgResourceIdentifier featureSrcResourceId;
+private MgFeatureService featureService;
+
+stringCollection = new MgStringCollection();
+queryOptions = new MgFeatureQueryOptions();
+// the feature source has already been installed in the repository
+featureSrcResourceId = new MgResourceIdentifier("Library://PlatformApiDocTests/SdfFeatureClass.FeatureSource");
+wktReaderWriter = new MgWktReaderWriter();
+```
+
+
+### `<Identifier> NULL`
+
+SometimesNULL is a string property. If you have not given a
+value to it when inserting certain features and you apply
+the filter, sometimesNULL NULL, you select those
+features. If you have given a value to it when inserting other
+features and you apply the filter, NOT sometimesNULL
+NULL, you select those other features.
+
+```
+sqlplus> select sometimesnull from featclass where sometimesnull is null;
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("sometimesNULL");
+$queryOptions->SetFilter("sometimesNULL NULL");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("SometimesNull");
+queryOptions.SetFilter("SometimesNull NULL");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+```
+sqlplus> select sometimesnull from featclass where sometimesnull is not null;
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("sometimesNULL");
+$queryOptions->SetFilter("NOT sometimesNULL NULL");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("SometimesNull");
+queryOptions.SetFilter("NOT SometimesNull NULL");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+### `<Identifier> LIKE <String>`
+
+Identifier is the name of a property whose type is
+MgPropertyType::String. String contains a pattern. A percent
+character (%) in a pattern matches zero or more characters.
+An underscore character (_) matches one character.
+
+Description is a string property. There are 2 features with
+this property in the datastore, and the contents of the two
+properties are: "POINT XY (1 1)" and "POLYGON XY ((0 0, 2 0, 2 2, 0 2, 0 0))".
+
+The filter, Description LIKE '%POLYGON%', returns
+"POLYGON XY ((0 0, 2 0, 2 2, 0 2, 0 0))", the filter,
+NOT Description LIKE '%POLYGON%', returns
+"POINT XY (1 1)", and the filter, Description LIKE
+'%POL_GON%', returns "POLYGON XY ((0 0, 2 0, 2 2, 0
+2, 0 0))".
+
+```
+sqlplus> select Description from featclass where Description LIKE '%POLYGON%';
+```
+
+PHP code
+
+```PHP
+$queryOptions->AddFeatureProperty("Description");
+$queryOptions->SetFilter("Description LIKE '%POLYGON%'");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("Description");
+queryOptions.SetFilter("Description LIKE '%POLYGON%'");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+```
+sqlplus> select Description from featclass where Description NOT LIKE '%POLYGON%';
+```
+
+PHP code
+
+```PHP
+$queryOptions->AddFeatureProperty("Description");
+$queryOptions->SetFilter("NOT Description LIKE '%POLYGON%'");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("Description");
+queryOptions.SetFilter("NOT Description LIKE '%POLYGON%'");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+```
+sqlplus> select Description from featclass where Description LIKE '%POL_GON%';
+```
+
+PHP code
+
+```PHP
+$queryOptions->AddFeatureProperty("Description");
+$queryOptions->SetFilter("Description LIKE '%POL_GON%'");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("Description");
+queryOptions.SetFilter("Description LIKE '%POL_GON%'");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+### `<Identifier> IN ( <ValueExpressionCollection> )`
+
+anInt16 is an Int16 property. In one feature instance the
+value of anInt16 is -7033. If you apply <filter>anInt16 IN
+( -5995, -7033 ), you select this feature.
+
+```
+sqlplus> select anInt16 from featclass where anInt16 in ( -5995, -7033 );
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("anInt16");
+$queryOptions->SetFilter("anInt16 IN ( -5995, -7033 )");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("anInt16");
+queryOptions.SetFilter("anInt16 IN ( -5995, -7033 )");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+### `<Identifier>  >  <DataValue>`
+
+featid is an identity property. If you apply the filter, featid
+> 20, you select the features whose featid has a
+value > 20. If you apply the filter, featid > 0 AND featid <
+5, you select the features whose featid belongs to
+{ 1, 2, 3, 4}. If you apply the filter, featid < 3 OR featid
+> 3, you select features whose featid is not 3.
+
+aDateTime is a date property. There is a feature whose
+aDateTime property has the value 9/20/2005::10:9:34:0. If you
+apply the filter, aDateTime < '2005-09-21', you
+select this feature.
+
+```
+sqlplus> select anInt16 from featclass where adatetime < '21-SEP-05';
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("anInt16");
+$queryOptions->SetFilter("aDateTime < '2005-09-21'");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("anInt16");
+queryOptions.SetFilter("aDateTime < '2005-09-21'");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+### `<Expression>  <  <DataValue>`
+
+anInt16 is an Int16 property. Two features have non-NULL
+values for this property. One has a value -7033, and the
+other -5995. If you apply the filter, ( anInt16 + 1000 ) <
+-5995, you select the feature whose anInt16
+property has the value -7033. The parentheses in this filter
+are optional because operator precedence would dictate that
+the filter, anInt16 + 1000 < -5995, is equivalent.
+
+```
+sqlplus> select anInt16 from featclass where anInt16 + 1000 < -5995;
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("anInt16");
+$queryOptions->SetFilter("anInt16 + 1000 < -5995");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("anInt16");
+queryOptions.SetFilter("anInt16 + 1000 < -5995");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+### `<FunctionValue> = <DataValue>`
+
+aDouble is a double property. One feature has aDouble
+property with a value of 8103.08393. If you apply
+the filter, ceil(aDouble) = 8104, you select this
+feature.
+
+```
+sqlplus> select aDouble from featclass where ceil(aDouble) = 8104;
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("aDouble");
+$queryOptions->SetFilter("ceil(aDouble) = 8104");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("aDouble");
+queryOptions.SetFilter("ceil(aDouble) = 8104");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+### Group Function
+
+aDouble is a double property. sum is a group function.
+sum(aDouble) sums the values of the aDouble property taken
+from a group of features.
+
+```
+sqlplus> select sum(aDouble) from featclass;
+```
+
+PHP code
+
+```php
+<?php
+$queryOptions->AddComputedProperty("sumDbl", "sum(aDouble)");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddComputedProperty("sumDbl", "sum(aDouble)");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process featureReader
+featureReader.Close();
+```
+
+### Ordering
+
+aDouble is a double property. anInt32Key is the identity
+property. The first example returns aDouble values in
+ascending order, and the second example returns them in
+descending order.
+
+#### Ascending
+
+```
+sqlplus> select anint32key,adouble from tuxuniversalclassxy order by adouble ASC;
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("aDouble");
+$queryOptions->AddFeatureProperty("anInt32Key");
+$stringCollection->Add("aDouble");
+$queryOptions->SetOrderingFilter($stringCollection, MgOrderingOption::Ascending);
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("aDouble");
+queryOptions.AddFeatureProperty("anInt32Key");
+stringCollection.Add("aDouble");
+queryOptions.SetOrderingFilter(stringCollection, MgOrderingOption::Ascending);
+featureReader = $featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process $featureReader
+featureReader.Close();
+```
+
+#### Descending
+
+```
+sqlplus> select anint32key,adouble from tuxuniversalclassxy order by adouble DESC;
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("aDouble");
+$queryOptions->AddFeatureProperty("anInt32Key");
+$stringCollection->Add("aDouble");
+$queryOptions->SetOrderingFilter($stringCollection, MgOrderingOption::Descending);
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("aDouble");
+queryOptions.AddFeatureProperty("anInt32Key");
+stringCollection.Add("aDouble");
+queryOptions.SetOrderingFilter(stringCollection, MgOrderingOption::Descending);
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process $featureReader
+featureReader.Close();
+```
+
+### Basic Filter OR Spatial Filter
+
+featId is an identity property, and geometry is a geometry
+property. The feature whose featId value is 0 has a geometry
+value of POINT(1 1). The feature whose featid value is 1 has
+a null geometry value. The spatial filter requests features
+whose geometry intersects with POINT(1 1). The following
+select operation returns both of these features. The
+operation can be coded in two ways. The first way uses the
+SetFilter(), SetSpatialFilter() and SetBinaryOperator()
+methods, and the second way uses only the SetFilter() method.
+
+```
+sqlplus> select a.featId from featclass a where a.featId = 1 or sdo_relate(a.geometry, MDSYS.SDO_GEOMETRY(2001, NULL, MDSYS.SDO_POINT_TYPE(1,1,NULL), NULL, NULL), 'mask=anyinteract') = 'TRUE';
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("featId");
+$queryOptions->SetFilter("featId = 1");
+$queryOptions->SetBinaryOperator(false);
+$geometry = $wktReaderWriter->Read("POINT(1 1)");
+$queryOptions->SetSpatialFilter("geometry", $geometry, MgFeatureSpatialOperations::Intersects);
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("featId");
+queryOptions.SetFilter("featId = 1");
+queryOptions.SetBinaryOperator(false);
+geometry = wktReaderWriter.Read("POINT(1 1)");
+queryOptions.SetSpatialFilter("geometry", geometry, MgFeatureSpatialOperations::Intersects);
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process $featureReader
+featureReader.Close();
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("featId");
+$queryOptions->SetFilter("(featId = 1) OR (geometry INTERSECTS GEOMFROMTEXT ( 'POINT(1 1)' )");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+```C#
+queryOptions.AddFeatureProperty("featId");
+geometry = wktReaderWriter.Read("POINT(1 1)");
+queryOptions.SetFilter("(featId = 1) OR (geometry INTERSECTS GEOMFROMTEXT ( 'POINT(1 1)' )");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process $featureReader
+featureReader.Close();
+```
+
+### Distance Filter
+
+featId is an identity property, and geometry is a geometry
+property. The feature whose featId value is 0 has a geometry
+value of POINT(1 1). The distance filter requests features
+whose geometry is within a distance of 1 from POINT(2 1). The
+following select operation returns the feature whose featId
+is 0.
+
+```
+sqlplus> select a.featId from featclass a where sdo_within_distance(a.geometry, MDSYS SDO_GEOMETRY(2001, NULL, MDSYS.SDO_POINT_TYPE(2,1 NULL), NULL, NULL), 'distance=1') = 'TRUE';
+```
+
+PHP code
+
+```PHP
+<?php
+$queryOptions->AddFeatureProperty("featId");
+$queryOptions->SetFilter("geometry WITHINDISTANCE GEOMFROMTEXT ('POINT(2 1)') 1");
+$featureReader = $featureService->SelectFeatures($featureSrcResourceId, $featClassName, $queryOptions);
+# process $featureReader
+$featureReader->Close();
+?>
+```
+
+C# code
+
+``` C#
+queryOptions.AddFeatureProperty("featId");
+queryOptions.SetFilter("geometry WITHINDISTANCE GEOMFROMTEXT ('POINT(2 1)') 1");
+featureReader = featureService.SelectFeatures(featureSrcResourceId, featClassName, queryOptions);
+// process $featureReader
+featureReader.Close();
+```

Added: branches/4.0/MgDev/Doc/landing/topics/feature-intro.md.tpl
===================================================================
--- branches/4.0/MgDev/Doc/landing/topics/feature-intro.md.tpl	                        (rev 0)
+++ branches/4.0/MgDev/Doc/landing/topics/feature-intro.md.tpl	2023-11-10 20:21:36 UTC (rev 10068)
@@ -0,0 +1,35 @@
+# Feature Service
+
+The Feature Service provides an abstraction layer for the
+storage and retrieval of feature data in a
+technology-independent way. You can use the API to determine
+which storage technologies are available and which
+capabilities they have. Access to the storage technology is
+modeled as a connection. For example, you can connect to a
+file and do simple insertions or connect to a relational
+database and do transaction-based operations.
+
+The root methods are contained in the `MgFeatureService` [!doclinkclass MgFeatureService] class.
+
+In general, you may do any of the following, subject to
+  provider capability limitations:
+  * Determine which storage technologies are available (see
+  `MgFeatureService::GetFeatureProviders()`),
+  * Determine which capabilities a feature provider has (see
+  `MgFeatureService::GetCapabilities()`),
+  * Verify the connection property values (see
+  `MgFeatureService::GetConnectionPropertyValues()`),
+  * Connect to a storage technology (feature provider) using
+  `MgFeatureService::TestConnection()`
+  * Describe the structure of feature data using ``MgFeatureService::DescribeSchema()`
+  * Determine the spatial contexts available in the datastore
+  (see `MgFeatureService::GetSpatialContexts()`),
+  * Insert feature data (see `MgInsertFeatures` [!doclinkclass MgInsertFeatures]),
+  * Select feature data (see
+  MgFeatureService::SelectFeatures() and
+  MgFeatureService::SelectAggregate()),
+  * Update feature data (see `MgUpdateFeatures` [!doclinkclass MgUpdateFeatures]),
+  * Delete feature data (see `MgDeleteFeatures` [!doclinkclass MgDeleteFeatures]),
+  * Execute SQL commands (see
+  `MgFeatureService::ExecuteSqlQuery()` and
+  `MgFeatureService::ExecuteSqlNonQuery()`).

Added: branches/4.0/MgDev/Doc/landing/topics/feature-provider-registry.md.tpl
===================================================================
--- branches/4.0/MgDev/Doc/landing/topics/feature-provider-registry.md.tpl	                        (rev 0)
+++ branches/4.0/MgDev/Doc/landing/topics/feature-provider-registry.md.tpl	2023-11-10 20:21:36 UTC (rev 10068)
@@ -0,0 +1,122 @@
+# Feature Provider Registry
+
+The feature provider registry contains most importantly the
+name of each available provider, version information and the name of the provider DLL.
+The full set of registry information elements and attributes is specified
+in the XML schema [FeatureProviderRegistry](xmlschema-featureproviderregistry.md).
+
+The FeatureProviders constructor takes the XML string
+containing the provider registry as an argument. It creates
+an array of FeatureProvider objects. Currently it only parses
+the Name elements from the registry information. 
+
+PHP code
+
+```PHP
+<?php
+class FeatureProviders {
+   var $xpath;
+   var $featureProviders;
+   var $logFileHandle;
+   function arrayOfProviderNames() {
+       return array_keys($this->featureProviders);
+   }
+   function FeatureProviders( $logFileHandle, $byteReader ) {
+      $this->logFileHandle = $logFileHandle;
+      $doc = new DOMDocument();
+        $xmlStr = $byteReader->ToString();
+        $doc->loadXML($xmlStr);
+      $this->xpath = new DOMXPath($doc);
+      $this->queryProviders();
+   }
+   function queryProviders() {
+      $providers = $this->xpath->query("//FeatureProviderRegistry/FeatureProvider");
+      $count = $providers->length;
+      for($i=0; $i<$count; $i++) {
+             $provider = $providers->item($i);
+            $providerElements = $provider->childNodes;
+            $connectionProperties = NULL;
+             foreach($providerElements as $providerElement) {
+                $providerElemTagname = $providerElement->tagName;
+                if ($providerElemTagname == "Name") {
+                   $providerName = $providerElement->nodeValue;
+                   $featureProvider = new FeatureProvider($providerName);
+                } 
+             }
+           $this->featureProviders[$providerName] = $featureProvider;
+      }
+   }
+}
+class FeatureProvider {
+   var $providerName;
+   var $connectionProperties = NULL;
+   function FeatureProvider( $providerName ) {
+      $this->providerName = $providerName;
+   }
+   function GetProviderName() {
+      return $providerName;
+   }
+}
+?>
+```
+
+The GetProviderNames method gets the provider registry from Feature Services.
+The registry is in xml and the method uses an xpath expression to retrieve
+the provider names. It concatenates the provider names for use in a test expression.
+
+The following string is an example of the output from this program.
+
+```Autodesk.Oracle.3.2 Autodesk.Raster.3.2 Autodesk.SqlServer.3.2 OSGeo.ArcSDE.3.2 OSGeo.MySQL.3.2 OSGeo.ODBC.3.2 OSGeo.SDF.3.2 OSGeo.SHP.3.2 OSGeo.WFS.3.2 OSGeo.WMS.3.2```
+
+The utility method shows the use of System Xml classes to extract values
+from the xml.
+
+C# code
+
+```C#
+using OSGeo.MapGuide;
+// The MgFeatureService example code shows how the MgFeatureService object is created.
+private MgFeatureService featureService;
+private String providerNamesActual;
+
+private void GetProviderNames()
+{
+	System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo(0x0409);
+	String[] names;
+	MgByteReader byteReader = featureService.GetFeatureProviders();
+	String xmlContent = byteReader.ToString();
+	names = GetXpathValuesRtnStrArr(xmlContent,
+		"//FeatureProviderRegistry/FeatureProvider/Name");
+	providerCountActual = names.GetLength(0);
+	Array.Sort(names, StringComparer.Create(culture, false));
+	providerNamesActual = StringArrayToString(names);
+}
+
+private String[] GetXpathValuesRtnStrArr(String xmlContent, String xpath)
+{
+	System.Collections.ArrayList strList = new System.Collections.ArrayList(10);
+	String[] strArr;
+	System.Xml.XmlDocument xmlDocument = new XmlDocument();
+	xmlDocument.LoadXml(xmlContent);
+	XmlNodeList nodeList;
+	XmlElement root = xmlDocument.DocumentElement;
+	nodeList = root.SelectNodes(xpath);
+	int count = nodeList.Count;
+	if (count == 0)
+	{
+		strArr = new String[] { "" };
+		return strArr;
+	}
+	XmlNode node;
+	for (int i = 0; i < count; i++)
+	{
+		node = nodeList.Item(i);
+		strList.Add(node.FirstChild.Value);
+	}
+	strArr = (String[])strList.ToArray(typeof(string));
+	return strArr;
+}
+
+GetProviderNames();
+System.Console.WriteLine(providerNamesActual);
+```

Added: branches/4.0/MgDev/Doc/landing/topics/feature-provider.md.tpl
===================================================================
--- branches/4.0/MgDev/Doc/landing/topics/feature-provider.md.tpl	                        (rev 0)
+++ branches/4.0/MgDev/Doc/landing/topics/feature-provider.md.tpl	2023-11-10 20:21:36 UTC (rev 10068)
@@ -0,0 +1,2186 @@
+# Feature Providers
+
+## Provider Capabilities
+
+The MgFeatureService API delegates much of its functionality
+to FDO providers. The providers implement the storage and
+retrieval of feature data from a variety of RDBMS and
+file-based datastore technologies.
+
+A set of provider capabilities has been defined, and each
+provider has been characterized according to what
+capabilities it supports. For example, the FDO Provider for
+Oracle supports the creation, description, and destruction of
+a schema definition, but the FDO Provider for ArcSDE supports
+only the description of a schema definition.
+
+The capabilities are grouped in the following categories:
+<ul>
+<li>Connection</li>
+<li>Schema</li>
+<li>Commands</li>
+<li>Expressions</li>
+<li>Filters</li>
+<li>Raster</li>
+<li>Topology</li>
+</ul>
+
+The MgFeatureService::GetCapabilities method returns an XML
+representation of a provider's capabilities. See \link FdoProviderCapabilities_schema FdoProviderCapabilities \endlink
+for the definition of the XML representation.
+
+The capability characterization can be used to execute code
+conditionally depending on the provider being used and what
+capability is being executed.
+
+## Example (C#)
+
+The GetFeatureProviders method cyles through the list of provider names and
+creates a FeatureProvider object for each one.
+
+The FeatureProvider constructor uses the MgFeatureService object to get the
+capabilities for the provider.
+The capabilities are in xml and the Extract methods use xpath
+expressions to extract the capability values from the xml.
+
+The utility methods show the use of System Xml classes to extract values
+from the xml.
+
+Here are a few of the capability strings generated by this code for the SDF provider.
+
+<ul>
+	<li>ConnectionCapabilities:ThreadCapability=PerConnectionThreaded;SpatialContextExtentType=Dynamic;</li>
+	<li>CommandCapabilities:Commands=+Select+Insert+Delete+Update+DescribeSchema+ApplySchema+CreateSpatialContext+GetSpatialContexts+SelectAggregates;SelectExpressions;SelectFunctions;SelectDistinct;</li>
+	<li>SchemaCapabilities:ClassType=+Class+FeatureClass;DataType=+Boolean+Byte+DateTime+Decimal+Double+Int16+Int32+Int64+Single+String;Inheritance;AssociationProperties;AutoIdGeneration;SupportedAutoGeneratedTypes=+Int32;SchemaModification;</li>
+	<li>GeometryCapabilities:GeometryTypes=+Point+LineString+Polygon+MultiPoint+MultiLineString+MultiPolygon+MultiGeometry+CurveString+CurvePolygon+MultiCurveString+MultiCurvePolygon;ComponentType=+LinearRing+ArcSegment+LinearSegment+CurveRing;Dimensionality=XYZM;</li>
+	<li>FilterCapabilities:ConditionType=+Comparison+Like+In+Null+Spatial;SpatialOperation=+Contains+Disjoint+Intersects+Within+Inside+EnvelopeIntersects;DistanceOperation=;</li>
+	<li>ExpressionCapabilities:ExpressionType=+Basic+Function;FunctionDefinitions=+Concat+SpatialExtents+Ceil+Floor+Lower+Upper+Sum+Count+Min+Avg+Max;</li>
+</ul>
+
+C# code
+
+```C#
+using OSGeo.MapGuide;
+// The MgFeatureService example code shows how the MgFeatureService object is created.
+private MgFeatureService featureService;
+private String providerNamesActual;
+private ListDictionary featureProviders;
+
+private void GetProviderNames()
+{
+	System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo(0x0409);
+	String[] names;
+	MgByteReader byteReader = featureService.GetFeatureProviders();
+	String xmlContent = byteReader.ToString();
+	names = GetXpathValuesRtnStrArr(xmlContent,
+		"//FeatureProviderRegistry/FeatureProvider/Name");
+	providerCountActual = names.GetLength(0);
+	Array.Sort(names, StringComparer.Create(culture, false));
+	providerNamesActual = StringArrayToString(names);
+}
+
+private void GetFeatureProviders()
+{
+	featureProviders = new ListDictionary();
+	FeatureProvider featureProvider;
+	String providerName;
+	string[] names = providerNamesActual.Split();
+	for (int i = 0; i < providerCountActual; i++)
+	{
+		providerName = names[i];
+		featureProvider = new FeatureProvider(logger, providerName,
+			featureService, utilities, timings);
+		featureProviders.Add(providerName, featureProvider);
+	}
+}
+
+class FeatureProvider
+{
+	public FeatureProvider(...
+		MgFeatureService featureService,
+		Utilities utilities,
+		...)
+	{
+		...
+		MgByteReader byteReader = featureService.GetCapabilities(providerName);
+        xmlContent = byteReader.ToString();
+        ExtractConnectionCapabilities();
+        ExtractSchemaCapabilities();
+        ExtractCommandCapabilities();
+        ExtractFilterCapabilities();
+        ExtractExpressionCapabilities();
+        ExtractRasterCapabilities();
+        ExtractTopologyCapabilities();
+        ExtractGeometryCapabilities();
+    }
+    private MgFeatureService featureService;
+    private String xmlContent;
+    private String connectionCapabilitiesToStr;
+    private String schemaCapabilitiesToStr;
+    private String commandCapabilitiesToStr;
+    private String filterCapabilitiesToStr;
+    private String expressionCapabilitiesToStr;
+    private String rasterCapabilitiesToStr;
+    private String topologyCapabilitiesToStr;
+    private String geometryCapabilitiesToStr;
+    private StringBuilder sb;
+    private const int sblength = 640;
+    private Boolean multipleSchema;
+
+	// public property declarations to expose capability strings
+
+	private void ExtractConnectionCapabilities()
+	{
+		sb = new StringBuilder(sblength);
+		sb.Append("ConnectionCapabilities:");
+		String ThreadCapability = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/ThreadCapability");
+		sb.Append("ThreadCapability=" + ThreadCapability + ';');
+		String SpatialContextExtentType = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/SpatialContextExtent/Type");
+		sb.Append("SpatialContextExtentType=" + SpatialContextExtentType + ';');
+		String SupportsLocking = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/SupportsLocking");
+		if (SupportsLocking.Equals("true"))
+		{
+			sb.Append("Locking;");
+		}
+		String SupportsTimeout = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/SupportsTimeout");
+		if (SupportsTimeout.Equals("true"))
+		{
+			sb.Append("Timeout;");
+		}
+		String SupportsTransactions = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/SupportsTransactions");
+		if (SupportsTransactions.Equals("true"))
+		{
+			sb.Append("Transactions;");
+		}
+		String SupportsLongTransactions = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/SupportsLongTransactions");
+		if (SupportsLongTransactions.Equals("true"))
+		{
+			sb.Append("LongTransactions;");
+		}
+		String SupportsSQL = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/SupportsSQL");
+		if (SupportsSQL.Equals("true"))
+		{
+			sb.Append("SQL;");
+		}
+		String SupportsConfiguration = utilities.GetXpathValuesRtnStr(xmlContent, "Connection/SupportsConfiguration");
+		if (SupportsConfiguration.Equals("true"))
+		{
+			sb.Append("Configuration;");
+		}
+		connectionCapabilitiesToStr = sb.ToString();
+	}
+	private void ExtractSchemaCapabilities()
+	{
+		String value;
+		sb = new StringBuilder(sblength);
+		sb.Append("SchemaCapabilities:");
+		MgStringCollection ClassType = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Schema/Class/Type");
+		sb.Append("ClassType=");
+		int classCount = ClassType.GetCount();
+		for (int i = 0; i < classCount; i++)
+		{
+			sb.Append("+" + ClassType.GetItem(i));
+		}
+		sb.Append(";");
+		MgStringCollection DataType = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Schema/Data/Type");
+		sb.Append("DataType=");
+		int dataCount = DataType.GetCount();
+		for (int i = 0; i < dataCount; i++)
+		{
+			sb.Append("+" + DataType.GetItem(i));
+		}
+		sb.Append(";");
+		String SupportsInheritance = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsInheritance");
+		if (SupportsInheritance.Equals("true"))
+		{
+			sb.Append("Inheritance;");
+		}
+		String SupportsMultipleSchemas = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsMultipleSchemas");
+		if (SupportsMultipleSchemas.Equals("true"))
+		{
+			sb.Append("MultipleSchemas;");
+			multipleSchema = true;
+		}
+		else
+		{
+			multipleSchema = false;
+		}
+		String SupportsObjectProperties = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsObjectProperties");
+		if (SupportsObjectProperties.Equals("true"))
+		{
+			sb.Append("ObjectProperties;");
+		}
+		String SupportsAssociationProperties = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsAssociationProperties");
+		if (SupportsAssociationProperties.Equals("true"))
+		{
+			sb.Append("AssociationProperties;");
+		}
+		String SupportsSchemaOverrides = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsSchemaOverrides");
+		if (SupportsSchemaOverrides.Equals("true"))
+		{
+			sb.Append("SchemaOverrides;");
+		}
+		String SupportsNetworkModel = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsNetworkModel");
+		if (SupportsNetworkModel.Equals("true"))
+		{
+			sb.Append("NetworkModel;");
+		}
+		String SupportsAutoIdGeneration = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsAutoIdGeneration");
+		if (SupportsAutoIdGeneration.Equals("true"))
+		{
+			sb.Append("AutoIdGeneration;");
+		}
+		String SupportsDataStoreUniqueIdGeneration = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsDataStoreUniqueIdGeneration");
+		if (SupportsDataStoreUniqueIdGeneration.Equals("true"))
+		{
+			sb.Append("DataStoreUniqueIdGeneration;");
+		}
+		MgStringCollection SupportedAutoGeneratedTypes = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Schema/SupportedAutoGeneratedTypes/Type");
+		sb.Append("SupportedAutoGeneratedTypes=");
+		int autoGenTypeCount = SupportedAutoGeneratedTypes.GetCount();
+		for (int i = 0; i < autoGenTypeCount; i++)
+		{
+			value = SupportedAutoGeneratedTypes.GetItem(i);
+			if (value != "")
+			{
+				sb.Append("+" + value);
+			}
+		}
+		sb.Append(";");
+		String SupportsSchemaModification = utilities.GetXpathValuesRtnStr(xmlContent, "Schema/SupportsSchemaModification");
+		if (SupportsSchemaModification.Equals("true"))
+		{
+			sb.Append("SchemaModification;");
+		}
+		schemaCapabilitiesToStr = sb.ToString();
+	}
+	private void ExtractCommandCapabilities()
+	{
+		sb = new StringBuilder(sblength);
+		sb.Append("CommandCapabilities:");
+		MgStringCollection Commands = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Command/SupportedCommands/Name");
+		sb.Append("Commands=");
+		int cmdCount = Commands.GetCount();
+		for (int i = 0; i < cmdCount; i++)
+		{
+			sb.Append("+" + Commands.GetItem(i));
+		}
+		sb.Append(";");
+		String SupportsParameters = utilities.GetXpathValuesRtnStr(xmlContent, "Command/SupportsParameters");
+		if (SupportsParameters.Equals("true"))
+		{
+			sb.Append("Parameters;");
+		}
+		String SupportsTimeout = utilities.GetXpathValuesRtnStr(xmlContent, "Command/SupportsTimeout");
+		if (SupportsTimeout.Equals("true"))
+		{
+			sb.Append("Timeout;");
+		}
+		String SupportsSelectExpressions = utilities.GetXpathValuesRtnStr(xmlContent, "Command/SupportsSelectExpressions");
+		if (SupportsSelectExpressions.Equals("true"))
+		{
+			sb.Append("SelectExpressions;");
+		}
+		String SupportsSelectFunctions = utilities.GetXpathValuesRtnStr(xmlContent, "Command/SupportsSelectFunctions");
+		if (SupportsSelectFunctions.Equals("true"))
+		{
+			sb.Append("SelectFunctions;");
+		}
+		String SupportsSelectDistinct = GetXpathValuesRtnStr(xmlContent, "Command/SupportsSelectDistinct");
+		if (SupportsSelectDistinct.Equals("true"))
+		{
+			sb.Append("SelectDistinct;");
+		}
+		String SupportsSelectOrdering = utilities.GetXpathValuesRtnStr(xmlContent, "Command/SupportsSelectOrdering");
+		if (SupportsSelectOrdering.Equals("true"))
+		{
+			sb.Append("SelectOrdering;");
+		}
+		String SupportsSelectGrouping = utilities.GetXpathValuesRtnStr(xmlContent, "Command/SupportsSelectGrouping");
+		if (SupportsSelectGrouping.Equals("true"))
+		{
+			sb.Append("SelectGrouping;");
+		}
+		commandCapabilitiesToStr = sb.ToString();
+	}
+	private void ExtractFilterCapabilities()
+	{
+		String value;
+		sb = new StringBuilder(sblength);
+		sb.Append("FilterCapabilities:");
+		MgStringCollection ConditionType = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Filter/Condition/Type");
+		sb.Append("ConditionType=");
+		int conditionCount = ConditionType.GetCount();
+		for (int i = 0; i < conditionCount; i++)
+		{
+			value = ConditionType.GetItem(i);
+			if (value != "")
+			{
+				sb.Append("+" + value);
+			}
+		}
+		sb.Append(";");
+		MgStringCollection SpatialOperation = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Filter/Spatial/Operation");
+		sb.Append("SpatialOperation=");
+		int spatialOpCount = SpatialOperation.GetCount();
+		for (int i = 0; i < spatialOpCount; i++)
+		{
+			value = SpatialOperation.GetItem(i);
+			if (value != "")
+			{
+				sb.Append("+" + value);
+			}
+		}
+		sb.Append(";");
+		MgStringCollection DistanceOperation = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Filter/Distance/Operation");
+		sb.Append("DistanceOperation=");
+		int distanceOpCount = DistanceOperation.GetCount();
+		for (int i = 0; i < distanceOpCount; i++)
+		{
+			value = DistanceOperation.GetItem(i);
+			if (value != "")
+			{
+				sb.Append("+" + value);
+			}
+		}
+		sb.Append(";");
+		String SupportsGeodesicDistance = utilities.GetXpathValuesRtnStr(xmlContent, "Filter/SupportsGeodesicDistance");
+		if (SupportsGeodesicDistance.Equals("true"))
+		{
+			sb.Append("GeodesicDistance;");
+		}
+		String SupportsNonLinearGeometricOperations = utilities.GetXpathValuesRtnStr(xmlContent, "Filter/SupportsNonLinearGeometricOperations");
+		if (SupportsNonLinearGeometricOperations.Equals("true"))
+		{
+			sb.Append("NonLinearGeometricOperations;");
+		}
+		filterCapabilitiesToStr = sb.ToString();
+	}
+	private void ExtractExpressionCapabilities()
+	{
+		String value;
+		sb = new StringBuilder(sblength);
+		sb.Append("ExpressionCapabilities:");
+		MgStringCollection ExpressionType = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Expression/Type/Name");
+		sb.Append("ExpressionType=");
+		int typeCount = ExpressionType.GetCount();
+		for (int i = 0; i < typeCount; i++)
+		{
+			sb.Append("+" + ExpressionType.GetItem(i));
+		}
+		sb.Append(";");
+		MgStringCollection FunctionDefinitions = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Expression/FunctionDefinitionCollection/FunctionDefinition/Name");
+		sb.Append("FunctionDefinitions=");
+		int functionCount = FunctionDefinitions.GetCount();
+		for (int i = 0; i < functionCount; i++)
+		{
+			value = FunctionDefinitions.GetItem(i);
+			if (value != "")
+			{
+				sb.Append("+" + value);
+			}
+		}
+		sb.Append(";");
+		expressionCapabilitiesToStr = sb.ToString();
+	}
+	private void ExtractRasterCapabilities()
+	{
+		sb = new StringBuilder(sblength);
+		sb.Append("RasterCapabilities:");
+		String SupportsRaster = utilities.GetXpathValuesRtnStr(xmlContent, "Raster/SupportsRaster");
+		if (SupportsRaster.Equals("true"))
+		{
+			sb.Append("Raster;");
+		}
+		String SupportsStitching = utilities.GetXpathValuesRtnStr(xmlContent, "Raster/SupportsStitching");
+		if (SupportsStitching.Equals("true"))
+		{
+			sb.Append("Stitching;");
+		}
+		String SupportsSubsmpling = utilities.GetXpathValuesRtnStr(xmlContent, "Raster/SupportsSubsmpling");
+		if (SupportsSubsmpling.Equals("true"))
+		{
+			sb.Append("Subsmpling;");
+		}
+		rasterCapabilitiesToStr = sb.ToString();
+	}
+	private void ExtractTopologyCapabilities()
+	{
+		sb = new StringBuilder(sblength);
+		sb.Append("TopologyCapabilities:");
+		String SupportsTopology = utilities.GetXpathValuesRtnStr(xmlContent, "Topology/SupportsTopology");
+		if (SupportsTopology.Equals("true"))
+		{
+			sb.Append("Topology;");
+		}
+		String SupportsTopologicalHierarchy = utilities.GetXpathValuesRtnStr(xmlContent, "Topology/SupportsTopologicalHierarchy");
+		if (SupportsTopologicalHierarchy.Equals("true"))
+		{
+			sb.Append("TopologicalHierarchy;");
+		}
+		String BreaksCurveCrossingsAutomatically = utilities.GetXpathValuesRtnStr(xmlContent, "Topology/BreaksCurveCrossingsAutomatically");
+		if (BreaksCurveCrossingsAutomatically.Equals("true"))
+		{
+			sb.Append("BreaksCurveCrossingsAutomatically;");
+		}
+		String ActivatesTopologyByArea = utilities.GetXpathValuesRtnStr(xmlContent, "Topology/ActivatesTopologyByArea");
+		if (ActivatesTopologyByArea.Equals("true"))
+		{
+			sb.Append("ActivatesTopologyByArea;");
+		}
+		String ConstrainsFeatureMovements = utilities.GetXpathValuesRtnStr(xmlContent, "Topology/ConstrainsFeatureMovements");
+		if (ConstrainsFeatureMovements.Equals("true"))
+		{
+			sb.Append("ConstrainsFeatureMovements;");
+		}
+		topologyCapabilitiesToStr = sb.ToString();
+	}
+	private void ExtractGeometryCapabilities()
+	{
+		sb = new StringBuilder(sblength);
+		sb.Append("GeometryCapabilities:");
+		MgStringCollection GeometryTypes = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Geometry/Types/Type");
+		sb.Append("GeometryTypes=");
+		int geomTypeCount = GeometryTypes.GetCount();
+		for (int i = 0; i < geomTypeCount; i++)
+		{
+			sb.Append("+" + GeometryTypes.GetItem(i));
+		}
+		sb.Append(";");
+		MgStringCollection ComponentType = utilities.GetXpathValuesRtnMgStrColl(xmlContent, "Geometry/Components/Type");
+		sb.Append("ComponentType=");
+		int compTypeCount = ComponentType.GetCount();
+		for (int i = 0; i < compTypeCount; i++)
+		{
+			sb.Append("+" + ComponentType.GetItem(i));
+		}
+		sb.Append(";");
+		String Dimensionality = utilities.GetXpathValuesRtnStr(xmlContent, "Geometry/Dimensionality");
+		sb.Append("Dimensionality=" + MgCoordinateDimensionToStr(Convert.ToInt32(Dimensionality)) + ';');
+		geometryCapabilitiesToStr = sb.ToString();
+	}
+}
+
+public String[] GetXpathValuesRtnStrArr(String xmlContent, String xpath)
+{
+	System.Collections.ArrayList strList = new System.Collections.ArrayList(10);
+	String[] strArr;
+	System.Xml.XmlDocument xmlDocument = new XmlDocument();
+	xmlDocument.LoadXml(xmlContent);
+	XmlNodeList nodeList;
+	XmlElement root = xmlDocument.DocumentElement;
+	nodeList = root.SelectNodes(xpath);
+	int count = nodeList.Count;
+	if (count == 0)
+	{
+		strArr = new String[] { "" };
+		return strArr;
+	}
+	XmlNode node;
+	for (int i = 0; i < count; i++)
+	{
+		node = nodeList.Item(i);
+		strList.Add(node.FirstChild.Value);
+	}
+	strArr = (String[])strList.ToArray(typeof(string));
+	return strArr;
+}
+
+public String GetXpathValuesRtnStr(String xmlContent, String xpath)
+{
+	String value;
+	System.Xml.XmlDocument xmlDocument = new XmlDocument();
+	xmlDocument.LoadXml(xmlContent);
+	XmlElement root = xmlDocument.DocumentElement;
+	XmlNode node;
+	node = root.SelectSingleNode(xpath);
+	if (node == null)
+	{
+		value = "";
+		return value;
+	}
+	value = node.FirstChild.Value;
+	return value;
+}
+
+public MgStringCollection GetXpathValuesRtnMgStrColl(String xmlContent, String xpath)
+{
+	MgStringCollection strings = new MgStringCollection();
+	System.Xml.XmlDocument xmlDocument = new XmlDocument();
+	xmlDocument.LoadXml(xmlContent);
+	XmlNodeList nodeList;
+	XmlElement root = xmlDocument.DocumentElement;
+	nodeList = root.SelectNodes(xpath);
+	int count = nodeList.Count;
+	if (count == 0)
+	{
+		strings.Add("");
+		return strings;
+	}
+	XmlNode node;
+	for (int i = 0; i < count; i++)
+	{
+		node = nodeList.Item(i);
+		strings.Add(node.FirstChild.Value);
+	}
+	return strings;
+}
+
+```
+## Capabilities for Autodesk.Oracle.2.0
+
+What follows is a sample output from a PHP program used to get the
+capabilities for the Oracle provider. The program code follows the sample output.
+This program does the following:
+
+<ul>
+	<li>parses the XML returned by MgFeatureService::GetCapabilities()</li>
+	<li>implements a set of boolean capability functions</li>
+	<li>prints the values of the functions</li>
+</ul>
+
+
+```
+Connection
+    ThreadCapability
+        supportsSingleThreaded(): false
+        supportsPerConnectionThreaded(): true
+        supportsPerCommandThreaded(): false
+        supportsMultiThreaded(): false
+    SpatialContextExtent
+        Type
+            supportsDynamic(): false
+            supportsStatic(): true
+    supportsLocking(): true
+    supportsConnectionTimeout(): false
+    supportsTransactions(): true
+    supportsLongTransactions(): true
+    supportsSQL(): true
+    supportsConfiguration(): false
+Schema
+    Class
+        Type
+            supportsClass(): true
+            supportsFeatureClass(): true
+    Data
+        Type
+            supportsBoolean(): true
+            supportsByte(): true
+            supportsDateTime(): true
+            supportsDecimal(): true
+            supportsDouble(): true
+            supportsInt16(): true
+            supportsInt32(): true
+            supportsInt64(): true
+            supportsSingle(): true
+            supportsString(): true
+            supportsBLOB(): true
+            supportsCLOB(): true
+            supportsUniqueID(): false
+    supportsInheritance(): true
+    supportsMultipleSchemas(): true
+    supportsObjectProperties(): true
+    supportsAssociationProperties(): true
+    supportsSchemaOverrides(): true
+    supportsNetworkModel(): false
+    supportsAutoIdGeneration(): true
+    supportsDataStoreScopeUniqueIdGeneration(): true
+    supportsSchemaModification(): true
+Command
+    SupportedCommands
+        Name
+            supportsSelect(): true
+            supportsInsert(): true
+            supportsDelete(): true
+            supportsUpdate(): true
+            supportsSelectAggregates(): true
+            supportsDescribeSchema(): true
+            supportsDescribeSchemaMapping(): false
+            supportsDestroySchema(): true
+            supportsApplySchema(): true
+            supportsActivateSpatialContext(): true
+            supportsCreateSpatialContext(): true
+            supportsDestroySpatialContext(): true
+            supportsDestroySpatialContext(): true
+            supportsCreateMeasureUnit(): false
+            supportsDestroyMeasureUnit()t: false
+            supportsGetMeasureUnits(): false
+            supportsSQLCommand(): true
+            supportsAcquireLock(): true
+            supportsGetLockInfo(): true
+            supportsGetLockedObjects(): true
+            supportsGetLockOwners(): true
+            supportsReleaseLock(): true
+            supportsActivateLongTransaction(): true
+            supportsCommitLongTransaction(): true
+            supportsCreateLongTransaction(): true
+            supportsGetLongTransactions(): true
+            supportsFreezeLongTransaction(): false
+            supportsRollbackLongTransaction(): true
+            supportsActivateLongTransactionCheckpoint(): false
+            supportsCreateLongTransactionCheckpoint(): false
+            supportsGetLongTransactionCheckpoints(): false
+            supportsRollbackLongTransactionCheckpoint(): false
+            supportsChangeLongTransactionPrivileges(): false
+            supportsGetLongTransactionPrivileges(): false
+            supportsChangeLongTransactionSet(): false
+            supportsGetLongTransactionsInSet(): false
+            supportsFirstProviderCommand(): false
+            supportsDeactivateLongTransaction(): true
+    supportsParameters(): false
+    supportsCommandTimeout(): false
+    supportsSelectExpressions(): true
+    supportsSelectFunctions(): true
+    supportsSelectDistinct(): true
+    supportsSelectOrdering(): true
+    supportsSelectGrouping(): true
+Filter
+    Condition
+        Type
+            supportsComparison(): true
+            supportsLike(): true
+            supportsIn(): true
+            supportsNull(): true
+            supportsSpatial(): true
+            supportsDistance(): true
+    Spatial
+        Operation
+            supportsContains(): false
+            supportsCrosses(): false
+            supportsDisjoint(): false
+            supportsEquals(): false
+            supportsIntersects(): true
+            supportsOverlaps()s: false
+            supportsTouches(): false
+            supportsSpatialWithin(): false
+            supportsCoveredBy(): true
+            supportsInside(): true
+            supportsEnvelopeIntersects(): true
+    Distance
+        Operation
+            supportsDistanceWithin(): true
+            supportsBeyond(): false
+    supportsGeodesicDistance(): false
+    supportsNonLiteralGeometricOperations(): false
+Expression
+    Type
+        Name
+            supportsBasic(): true
+            supportsFunction(): true
+            supportsParameter(): true
+    FunctionDefinitionCollection
+        FunctionDefinition
+            Name
+                supportsAvg(): true
+                supportsCeil(): true
+                supportsCLIP(): false
+                supportsConcat(): true
+                supportsCount(): true
+                supportsFloor(): true
+                supportsLower(): true
+                supportsMax(): true
+                supportsMin(): true
+                supportsMOSAIC(): false
+                supportsStdDev(): false
+                supportsSum(): true
+                supportsUpper(): true
+Raster
+    supportsRaster(): false
+    supportsStitching(): false
+    supportsSubsampling(): false
+Topology
+    supportsTopology(): false
+    supportsTopologicalHierarchy(): false
+    supportsBreaksCurveCrossingsAutomatically(): false
+    supportsActivatesTopologyByArea(): false
+    supportsConstrainsFeatureMovements(): false
+Geometry
+    Type
+        supportsPoint(): true
+        supportsMultiPoint(): true
+        supportsLineString(): true
+        supportsMultiLineString(): true
+        supportsPolygon(): true
+        supportsMultiPolygon(): true
+        supportsCurveString(): true
+        supportsMultiCurveString(): true
+        supportsCurvePolygon(): true
+        supportsMultiCurvePolygon(): true
+        supportsMultiGeometry(): false
+    Components
+        Type
+            supportsLinearRing(): true
+            supportsLinearSegment(): true
+            supportsArcSegment(): true
+            supportsCurveRing(): true
+    dimensionality(): 3
+```
+
+PHP Example Code
+
+This code does the following:
+
+<ul>
+	<li>parses the XML returned by MgFeatureService::GetCapabilities()</li>
+	<li>implements a set of boolean capability functions</li>
+	<li>prints the values of the functions</li>
+</ul>
+
+The constructor takes a provider name, a file handle, and an
+MgByteReader containing the provider capabilities. The
+provider name is obtained from \link MgFeatureService::GetFeatureProviders MgFeatureService::GetFeatureProviders\endlink.
+The provider capabilities is obtained from 
+\link MgFeatureService::GetCapabilities MgFeatureService::GetCapabilities\endlink.
+The file handle is for logging.
+
+PHP code
+
+```PHP
+class ProviderCapabilities {
+   var $logFileHandle;
+   var $xpath;
+   var $providerName;
+
+   function ProviderCapabilities( $providerName, $logFileHandle, $byteReader ) {
+      try {
+     $this->providerName = $providerName;
+     $this->logFileHandle = $logFileHandle;
+     $doc = new DOMDocument();
+     $byteSink = new MgByteSink($byteReader);
+     $filePath = "temp_byte_reader_file.$providerName";
+//     fwrite($this->logFileHandle, "file path is \\"$filePath\\"\\n");
+     $byteSink->ToFile($filePath);
+     $doc->load($filePath);
+//     unlink($filePath);
+     $this->xpath = new DOMXPath($doc);
+     $this->queryConnection();
+     $this->querySchema();
+     $this->queryCommand();
+     $this->queryFilter();
+     $this->queryExpression();
+     $this->queryRaster();
+     $this->queryTopology();
+     $this->queryGeometry();
+      } catch (Exception $e) {
+     $msg = $e->GetMessage();
+     fwrite($logFileHandle, "ProviderCapabilities PHP Exception: $msg\\n");
+      }
+   }
+
+   function prtBool($bool) {
+      if ($bool) {
+     return "true";
+      } else {
+     return "false";
+      }
+   }
+
+   function queryBooleanElement($queryExpr, &$reference) {
+      $domNodeList = $this->xpath->query($queryExpr);
+      if ($domNodeList->length == 0) return;
+      $domNode = $domNodeList->item(0);
+      $value = $domNode->nodeValue;
+      switch($value) {
+     case "true" : $reference = TRUE; break;
+     case "false" : $reference = FALSE; break;
+     default : fwrite($this->logFileHandle, "$queryExpr: unknown value: $value\\n");
+      }
+   }
+
+/*
+   var $supports = FALSE;
+   function supports() {
+      return $this->supports;
+   }
+\*/
+
+   // CONNECTION CAPABILITIES
+   // thread capability
+
+   var $supportsSingleThreaded = FALSE;
+   function supportsSingleThreaded() {
+      return $this->supportsSingleThreaded;
+   }
+
+   var $supportsPerConnectionThreaded = FALSE;
+   function supportsPerConnectionThreaded() {
+      return $this->supportsPerConnectionThreaded;
+   }
+
+   var $supportsPerCommandThreaded = FALSE;
+   function supportsPerCommandThreaded() {
+      return $this->supportsPerCommandThreaded;
+   }
+
+   var $supportsMultiThreaded = FALSE;
+   function supportsMultiThreaded() {
+      return $this->supportsMultiThreaded;
+   }
+
+   // spatial context extent type
+
+   var $supportsDynamic = FALSE;
+   function supportsDynamic() {
+      return $this->supportsDynamic;
+   }
+
+   var $supportsStatic = FALSE;
+   function supportsStatic() {
+      return $this->supportsStatic;
+   }
+
+   // rest of connection capabilities
+   var $supportsLocking = FALSE;
+   function supportsLocking() {
+      return $this->supportsLocking;
+   }
+
+   var $supportsConnectionTimeout = FALSE;
+   function supportsConnectionTimeout() {
+      return $this->supportsConnectionTimeout;
+   }
+
+   var $supportsTransactions = FALSE;
+   function supportsTransactions() {
+      return $this->supportsTransactions;
+   }
+
+   var $supportsLongTransactions = FALSE;
+   function supportsLongTransactions() {
+      return $this->supportsLongTransactions;
+   }
+
+   var $supportsSQL = FALSE;
+   function supportsSQL() {
+      return $this->supportsSQL;
+   }
+
+   var $supportsConfiguration = FALSE;
+   function supportsConfiguration() {
+      return $this->supportsConfiguration;
+   }
+
+   function queryConnection() {
+      $this->queryThreadCapability();
+      $this->querySpatialContextExtentType();
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Connection/SupportsLocking", $this->supportsLocking);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Connection/SupportsTimeout", $this->supportsConnectionTimeout);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Connection/SupportsTransactions", $this->supportsTransactions);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Connection/SupportsLongTransactions", $this->supportsLongTransactions);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Connection/SupportsSQL", $this->supportsSQL);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Connection/SupportsConfiguration", $this->supportsConfiguration);
+   }
+
+   function queryThreadCapability() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Connection/ThreadCapability");
+      if ($domNodeList->length == 0) return;
+      $domNode = $domNodeList->item(0);
+      $value = $domNode->nodeValue;
+      switch($value) {
+     case "PerConnectionThreaded" : $this->supportsPerConnectionThreaded = TRUE; break;
+     case "SingleThreaded" : $this->supportsSingleThreaded = TRUE; break;
+     case "PerCommandThreaded" : $this->supportsPerCommandThreaded = TRUE; break;
+     case "MultiThreaded" : $this->supportsMultiThreaded = TRUE; break;
+     default : fwrite($this->logFileHandle, "unknown thread type: $value\\n");
+      }
+   }
+
+   function querySpatialContextExtentType() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Connection/SpatialContextExtent/Type");
+      if ($domNodeList->length == 0) return;
+      $domNode = $domNodeList->item(0);
+      $value = $domNode->nodeValue;
+      switch($value) {
+     case "Static" : $this->supportsStatic = TRUE; break;
+     case "Dynamic" : $this->supportsDynamic = TRUE; break;
+     default : fwrite($this->logFileHandle, "unknown spatial context extent type: $value\\n");
+      }
+   }
+
+   // SCHEMA
+   // class type
+   var $supportsFeatureClass = FALSE;
+   function supportsFeatureClass() {
+      return $this->supportsFeatureClass;
+   }
+
+   var $supportsClass = FALSE;
+   function supportsClass() {
+      return $this->supportsClass;
+   }
+
+   // data types
+   var $supportsBoolean = FALSE;
+   function supportsBoolean() {
+      return $this->supportsBoolean;
+   }
+
+   var $supportsByte = FALSE;
+   function supportsByte() {
+      return $this->supportsByte;
+   }
+
+   var $supportsDateTime = FALSE;
+   function supportsDateTime() {
+      return $this->supportsDateTime;
+   }
+
+   var $supportsDecimal = FALSE;
+   function supportsDecimal() {
+      return $this->supportsDecimal;
+   }
+
+   var $supportsDouble = FALSE;
+   function supportsDouble() {
+      return $this->supportsDouble;
+   }
+
+   var $supportsInt16 = FALSE;
+   function supportsInt16() {
+      return $this->supportsInt16;
+   }
+
+   var $supportsInt32 = FALSE;
+   function supportsInt32() {
+      return $this->supportsInt32;
+   }
+
+   var $supportsInt64 = FALSE;
+   function supportsInt64() {
+      return $this->supportsInt64;
+   }
+
+   var $supportsSingle = FALSE;
+   function supportsSingle() {
+      return $this->supportsSingle;
+   }
+
+   var $supportsString = FALSE;
+   function supportsString() {
+      return $this->supportsString;
+   }
+
+   var $supportsBLOB = FALSE;
+   function supportsBLOB() {
+      return $this->supportsBLOB;
+   }
+
+   var $supportsCLOB = FALSE;
+   function supportsCLOB() {
+      return $this->supportsCLOB;
+   }
+
+   var $supportsUniqueID = FALSE;
+   function supportsUniqueID() {
+      return $this->supportsUniqueID;
+   }
+
+   // rest of schema
+
+   var $supportsInheritance = FALSE;
+   function supportsInheritance() {
+      return $this->supportsInheritance;
+   }
+
+   var $supportsMultipleSchemas = FALSE;
+   function supportsMultipleSchemas() {
+      return $this->supportsMultipleSchemas;
+   }
+
+   var $supportsObjectProperties = FALSE;
+   function supportsObjectProperties() {
+      return $this->supportsObjectProperties;
+   }
+
+   var $supportsAssociationProperties = FALSE;
+   function supportsAssociationProperties() {
+      return $this->supportsAssociationProperties;
+   }
+
+   var $supportsSchemaOverrides = FALSE;
+   function supportsSchemaOverrides() {
+      return $this->supportsSchemaOverrides;
+   }
+
+   var $supportsNetworkModel = FALSE;
+   function supportsNetworkModel() {
+      return $this->supportsNetworkModel;
+   }
+
+   var $supportsAutoIdGeneration = FALSE;
+   function supportsAutoIdGeneration() {
+      return $this->supportsAutoIdGeneration;
+   }
+
+   var $supportsDataStoreScopeUniqueIdGeneration = FALSE;
+   function supportsDataStoreScopeUniqueIdGeneration() {
+      return $this->supportsDataStoreScopeUniqueIdGeneration;
+   }
+
+   var $supportsSchemaModification = FALSE;
+   function supportsSchemaModification() {
+      return $this->supportsSchemaModification;
+   }
+
+   function querySchema() {
+      $this->queryClassTypes();
+      $this->queryDataTypes();
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsInheritance", $this->supportsInheritance);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsMultipleSchemas", $this->supportsMultipleSchemas);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsObjectProperties", $this->supportsObjectProperties);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsAssociationProperties", $this->supportsAssociationProperties);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsSchemaOverrides", $this->supportsSchemaOverrides);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsNetworkModel", $this->supportsNetworkModel);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsAutoIdGeneration", $this->supportsAutoIdGeneration);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsDataStoreScopeUniqueIdGeneration", $this->supportsDataStoreScopeUniqueIdGeneration);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Schema/SupportsSchemaModification", $this->supportsSchemaModification);
+   }
+
+   function queryClassTypes() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Schema/Class/Type");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Class" : $this->supportsClass = TRUE; break;
+        case "FeatureClass" : $this->supportsFeatureClass = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown schema class type: $value\\n");
+     }
+      }
+   }
+
+   function queryDataTypes() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Schema/Data/Type");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Boolean" : $this->supportsBoolean = TRUE; break;
+        case "Byte" : $this->supportsByte = TRUE; break;
+        case "DateTime" : $this->supportsDateTime = TRUE; break;
+        case "Decimal" : $this->supportsDecimal = TRUE; break;
+        case "Double" : $this->supportsDouble = TRUE; break;
+        case "Int16" : $this->supportsInt16 = TRUE; break;
+        case "Int32" : $this->supportsInt32 = TRUE; break;
+        case "Int64" : $this->supportsInt64 = TRUE; break;
+        case "Single" : $this->supportsSingle = TRUE; break;
+        case "String" : $this->supportsString = TRUE; break;
+        case "CLOB" : $this->supportsCLOB = TRUE; break;
+        case "BLOB" : $this->supportsBLOB = TRUE; break;
+        case "UniqueID" : $this->supportsUniqueID = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown data type: $value\\n");
+     }
+      }
+   }
+
+   // COMMAND
+
+   var $supportsSelect = FALSE;
+   function supportsSelect() {
+      return $this->supportsSelect;
+   }
+
+   var $supportsSelectAggregates = FALSE;
+   function supportsSelectAggregates() {
+      return $this->supportsSelectAggregates;
+   }
+
+   var $supportsInsert = FALSE;
+   function supportsInsert() {
+      return $this->supportsInsert;
+   }
+
+   var $supportsDelete = FALSE;
+   function supportsDelete() {
+      return $this->supportsDelete;
+   }
+
+   var $supportsUpdate = FALSE;
+   function supportsUpdate() {
+      return $this->supportsUpdate;
+   }
+
+   var $supportsDescribeSchema = FALSE;
+   function supportsDescribeSchema() {
+      return $this->supportsDescribeSchema;
+   }
+
+   var $supportsDescribeSchemaMapping = FALSE;
+   function supportsDescribeSchemaMapping() {
+      return $this->supportsDescribeSchemaMapping;
+   }
+
+   var $supportsApplySchema = FALSE;
+   function supportsApplySchema() {
+      return $this->supportsApplySchema;
+   }
+
+   var $supportsDestroySchema = FALSE;
+   function supportsDestroySchema() {
+      return $this->supportsDestroySchema;
+   }
+
+   var $supportsActivateSpatialContext = FALSE;
+   function supportsActivateSpatialContext() {
+      return $this->supportsActivateSpatialContext;
+   }
+   var $supportsCreateSpatialContext = FALSE;
+   function supportsCreateSpatialContext() {
+      return $this->supportsCreateSpatialContext;
+   }
+
+   var $supportsDestroySpatialContext = FALSE;
+   function supportsDestroySpatialContext() {
+      return $this->supportsDestroySpatialContext;
+   }
+
+   var $supportsGetSpatialContexts = FALSE;
+   function supportsGetSpatialContexts() {
+      return $this->supportsGetSpatialContexts;
+   }
+
+   var $supportsCreateMeasureUnit = FALSE;
+   function supportsCreateMeasureUnit() {
+      return $this->supportsCreateMeasureUnit;
+   }
+
+   var $supportsDestroyMeasureUnit = FALSE;
+   function supportsDestroyMeasureUnit() {
+      return $this->supportsDestroyMeasureUnit;
+   }
+
+   var $supportsGetMeasureUnits = FALSE;
+   function supportsGetMeasureUnits() {
+      return $this->supportsGetMeasureUnits;
+   }
+   
+   var $supportsSQLCommand = FALSE;
+   function supportsSQLCommand() {
+      return $this->supportsSQLCommand;
+   }
+
+   var $supportsAcquireLock = FALSE;
+   function supportsAcquireLock() {
+      return $this->supportsAcquireLock;
+   }
+
+   var $supportsGetLockInfo = FALSE;
+   function supportsGetLockInfo() {
+      return $this->supportsGetLockInfo;
+   }
+
+   var $supportsGetLockedObjects = FALSE;
+   function supportsGetLockedObjects() {
+      return $this->supportsGetLockedObjects;
+   }
+
+   var $supportsGetLockOwners = FALSE;
+   function supportsGetLockOwners() {
+      return $this->supportsGetLockOwners;
+   }
+
+   var $supportsReleaseLock = FALSE;
+   function supportsReleaseLock() {
+      return $this->supportsReleaseLock;
+   }
+
+   var $supportsActivateLongTransaction = FALSE;
+   function supportsActivateLongTransaction() {
+      return $this->supportsActivateLongTransaction;
+   }
+
+   var $supportsCommitLongTransaction = FALSE;
+   function supportsCommitLongTransaction() {
+      return $this->supportsCommitLongTransaction;
+   }
+
+   var $supportsCreateLongTransaction = FALSE;
+   function supportsCreateLongTransaction() {
+      return $this->supportsCreateLongTransaction;
+   }
+
+   var $supportsGetLongTransactions = FALSE;
+   function supportsGetLongTransactions() {
+      return $this->supportsGetLongTransactions;
+   }
+
+   var $supportsFreezeLongTransaction = FALSE;
+   function supportsFreezeLongTransaction() {
+      return $this->supportsFreezeLongTransaction;
+   }
+
+   var $supportsRollbackLongTransaction = FALSE;
+   function supportsRollbackLongTransaction() {
+      return $this->supportsRollbackLongTransaction;
+   }
+
+   var $supportsActivateLongTransactionCheckpoint = FALSE;
+   function supportsActivateLongTransactionCheckpoint() {
+      return $this->supportsActivateLongTransactionCheckpoint;
+   }
+
+   var $supportsCreateLongTransactionCheckpoint = FALSE;
+   function supportsCreateLongTransactionCheckpoint() {
+      return $this->supportsCreateLongTransactionCheckpoint;
+   }
+
+   var $supportsGetLongTransactionCheckpoints = FALSE;
+   function supportsGetLongTransactionCheckpoints() {
+      return $this->supportsGetLongTransactionCheckpoints;
+   }
+
+   var $supportsRollbackLongTransactionCheckpoint = FALSE;
+   function supportsRollbackLongTransactionCheckpoint() {
+      return $this->supportsRollbackLongTransactionCheckpoint;
+   }
+
+   var $supportsChangeLongTransactionPrivileges = FALSE;
+   function supportsChangeLongTransactionPrivileges() {
+      return $this->supportsChangeLongTransactionPrivileges;
+   }
+
+   var $supportsGetLongTransactionPrivileges = FALSE;
+   function supportsGetLongTransactionPrivileges() {
+      return $this->supportsGetLongTransactionPrivileges;
+   }
+
+   var $supportsChangeLongTransactionSet = FALSE;
+   function supportsChangeLongTransactionSet() {
+      return $this->supportsChangeLongTransactionSet;
+   }
+
+   var $supportsGetLongTransactionsInSet = FALSE;
+   function supportsGetLongTransactionsInSet() {
+      return $this->supportsGetLongTransactionsInSet;
+   }
+
+   var $supportsFirstProviderCommand = FALSE;
+   function supportsFirstProviderCommand() {
+      return $this->supportsFirstProviderCommand;
+   }
+
+   var $supportsDeactivateLongTransaction = FALSE;
+   function supportsDeactivateLongTransaction() {
+      return $this->supportsDeactivateLongTransaction;
+   }
+
+   // rest of Commands
+
+   var $supportsParameters = FALSE;
+   function supportsParameters() {
+      return $this->supportsParameters;
+   }
+
+   var $supportsCommandTimeout = FALSE;
+   function supportsCommandTimeout() {
+      return $this->supportsCommandTimeout;
+   }
+
+   var $supportsSelectExpressions = FALSE;
+   function supportsSelectExpressions() {
+      return $this->supportsSelectExpressions;
+   }
+
+   var $supportsSelectFunctions = FALSE;
+   function supportsSelectFunctions() {
+      return $this->supportsSelectFunctions;
+   }
+
+   var $supportsSelectDistinct = FALSE;
+   function supportsSelectDistinct() {
+      return $this->supportsSelectDistinct;
+   }
+
+   var $supportsSelectOrdering = FALSE;
+   function supportsSelectOrdering() {
+      return $this->supportsSelectOrdering;
+   }
+
+   var $supportsSelectGrouping = FALSE;
+   function supportsSelectGrouping() {
+      return $this->supportsSelectGrouping;
+   }
+
+   function queryCommand() {
+      $this->queryCommands();
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Command/SupportsParameters", $this->supportsParameters);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Command/SupportsTimeout", $this->supportsCommandTimeout);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Command/SupportsSelectExpressions", $this->supportsSelectExpressions);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Command/SupportsSelectFunctions", $this->supportsSelectFunctions);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Command/SupportsSelectDistinct", $this->supportsSelectDistinct);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Command/SupportsSelectOrdering", $this->supportsSelectOrdering);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Command/SupportsSelectGrouping", $this->supportsSelectGrouping);
+   }
+
+   function queryCommands() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Command/SupportedCommands/Name");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Insert" : $this->supportsInsert = TRUE; break;
+        case "Delete" : $this->supportsDelete = TRUE; break;
+        case "Update" : $this->supportsUpdate = TRUE; break;
+        case "Select" : $this->supportsSelect = TRUE; break;
+        case "SelectAggregates" : $this->supportsSelectAggregates = TRUE; break;
+        case "DescribeSchema" : $this->supportsDescribeSchema = TRUE; break;
+        case "DescribeSchemaMapping" : $this->supportsDescribeSchemaMapping = TRUE; break;
+        case "DestroySchema" : $this->supportsDestroySchema = TRUE; break;
+        case "ApplySchema" : $this->supportsApplySchema = TRUE; break;
+        case "SQLCommand" : $this->supportsSQLCommand = TRUE; break;
+        case "ActivateSpatialContext" : $this->supportsActivateSpatialContext = TRUE; break;
+        case "CreateSpatialContext" : $this->supportsCreateSpatialContext = TRUE; break;
+        case "DestroySpatialContext" : $this->supportsDestroySpatialContext = TRUE; break;
+        case "GetSpatialContexts" : $this->supportsGetSpatialContexts = TRUE; break;
+        case "CreateMeasureUnit" : $this->supportsCreateMeasureUnit = TRUE; break;
+        case "DestroyMeasureUnit" : $this->supportsDestroyMeasureUnit = TRUE; break;
+        case "GetMeasureUnits" : $this->supportsGetMeasureUnits = TRUE; break;
+        case "AcquireLock" : $this->supportsAcquireLock = TRUE; break;
+        case "GetLockInfo" : $this->supportsGetLockInfo = TRUE; break;
+        case "GetLockedObjects" : $this->supportsGetLockedObjects = TRUE; break;
+        case "GetLockOwners" : $this->supportsGetLockOwners = TRUE; break;
+        case "ReleaseLock" : $this->supportsReleaseLock = TRUE; break;
+        case "ActivateLongTransaction" : $this->supportsActivateLongTransaction = TRUE; break;
+        case "CommitLongTransaction" : $this->supportsCommitLongTransaction = TRUE; break;
+        case "CreateLongTransaction" : $this->supportsCreateLongTransaction = TRUE; break;
+        case "GetLongTransactions" : $this->supportsGetLongTransactions = TRUE; break;
+        case "FreezeLongTransaction" : $this->supportsFreezeLongTransaction = TRUE; break;
+        case "RollbackLongTransaction" : $this->supportsRollbackLongTransaction = TRUE; break;
+        case "ActivateLongTransactionCheckpoint" : $this->supportsActivateLongTransactionCheckpoint = TRUE; break;
+        case "CreateLongTransactionCheckpoint" : $this->supportsCreateLongTransactionCheckpoint = TRUE; break;
+        case "GetLongTransactionCheckpoints" : $this->supportsGetLongTransactionCheckpoints = TRUE; break;
+        case "RollbackLongTransactionCheckpoint" : $this->supportsRollbackLongTransactionCheckpoint = TRUE; break;
+        case "ChangeLongTransactionPrivileges" : $this->supportsChangeLongTransactionPrivileges = TRUE; break;
+        case "GetLongTransactionPrivileges" : $this->supportsGetLongTransactionPrivileges = TRUE; break;
+        case "ChangeLongTransactionSet" : $this->supportsChangeLongTransactionSet = TRUE; break;
+        case "GetLongTransactionsInSet" : $this->supportsGetLongTransactionsInSet = TRUE; break;
+        case "FirstProviderCommand" : $this->supportsFirstProviderCommand = TRUE; break;
+        case "DeactivateLongTransaction" : $this->supportsDeactivateLongTransaction = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown command type: $value\\n");
+     }
+      }
+   }
+
+   // FILTER
+
+   // Condition types
+
+   var $supportsComparison = FALSE;
+   function supportsComparison() {
+      return $this->supportsComparison;
+   }
+
+   var $supportsLike = FALSE;
+   function supportsLike() {
+      return $this->supportsLike;
+   }
+
+   var $supportsIn = FALSE;
+   function supportsIn() {
+      return $this->supportsIn;
+   }
+
+   var $supportsNull = FALSE;
+   function supportsNull() {
+      return $this->supportsNull;
+   }
+
+   var $supportsSpatial = FALSE;
+   function supportsSpatial() {
+      return $this->supportsSpatial;
+   }
+
+   var $supportsDistance = FALSE;
+   function supportsDistance() {
+      return $this->supportsDistance;
+   }
+
+   // Spatial types
+
+   var $supportsContains = FALSE;
+   function supportsContains() {
+      return $this->supportsContains;
+   }
+
+   var $supportsCrosses = FALSE;
+   function supportsCrosses() {
+      return $this->supportsCrosses;
+   }
+
+   var $supportsDisjoint = FALSE;
+   function supportsDisjoint() {
+      return $this->supportsDisjoint;
+   }
+
+   var $supportsEquals = FALSE;
+   function supportsEquals() {
+      return $this->supportsEquals;
+   }
+
+   var $supportsIntersects = FALSE;
+   function supportsIntersects() {
+      return $this->supportsIntersects;
+   }
+
+   var $supportsOverlaps = FALSE;
+   function supportsOverlaps() {
+      return $this->supportsOverlaps;
+   }
+
+   var $supportsTouches = FALSE;
+   function supportsTouches() {
+      return $this->supportsTouches;
+   }
+
+   var $supportsSpatialWithin = FALSE;
+   function supportsSpatialWithin() {
+      return $this->supportsSpatialWithin;
+   }
+
+   var $supportsCoveredBy = FALSE;
+   function supportsCoveredBy() {
+      return $this->supportsCoveredBy;
+   }
+
+   var $supportsInside = FALSE;
+   function supportsInside() {
+      return $this->supportsInside;
+   }
+
+   var $supportsEnvelopeIntersects = FALSE;
+   function supportsEnvelopeIntersects() {
+      return $this->supportsEnvelopeIntersects;
+   }
+
+   // Distance types
+
+   var $supportsBeyond = FALSE;
+   function supportsBeyond() {
+      return $this->supportsBeyond;
+   }
+
+   var $supportsDistanceWithin = FALSE;
+   function supportsDistanceWithin() {
+      return $this->supportsDistanceWithin;
+   }
+
+   // rest of Filter
+
+   var $supportsGeodesicDistance = FALSE;
+   function supportsGeodesicDistance() {
+      return $this->supportsGeodesicDistance;
+   }
+
+   var $supportsNonLiteralGeometricOperations = FALSE;
+   function supportsNonLiteralGeometricOperations() {
+      return $this->supportsNonLiteralGeometricOperations;
+   }
+
+   function queryFilter() {
+      $this->queryConditionType();
+      $this->querySpatialOperations();
+      $this->queryDistanceOperations();
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Filter/SupportsGeodesicDistance", $this->supportsGeodesicDistance);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Filter/SupportsNonLiteralGeometricOperations", $this->supportsNonLiteralGeometricOperations);
+   }
+
+   function queryConditionType() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Filter/Condition/Type");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Comparison" : $this->supportsComparison = TRUE; break;
+        case "Like" : $this->supportsLike = TRUE; break;
+        case "In" : $this->supportsIn = TRUE; break;
+        case "Null" : $this->supportsNull = TRUE; break;
+        case "Spatial" : $this->supportsSpatial = TRUE; break;
+        case "Distance" : $this->supportsDistance = TRUE; break;
+; break;
+        default : fwrite($this->logFileHandle, "unknown filter condition type: $value\\n");
+     }
+      }
+   }
+
+   function querySpatialOperations() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Filter/Spatial/Operation");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Contains" : $this->supportsContains = TRUE; break;
+        case "Crosses" : $this->supportsCrosses = TRUE; break;
+        case "Disjoint" : $this->supportsDisjoint = TRUE; break;
+        case "Equals" : $this->supportsEquals = TRUE; break;
+        case "Intersects" : $this->supportsIntersects = TRUE; break;
+        case "Overlaps" : $this->supportsOverlaps = TRUE; break;
+        case "Touches" : $this->supportsTouches = TRUE; break;
+        case "Within" : $this->supportsSpatialWithin = TRUE; break;
+        case "CoveredBy" : $this->supportsCoveredBy = TRUE; break;
+        case "Inside" : $this->supportsInside = TRUE; break;
+        case "EnvelopeIntersects" : $this->supportsEnvelopeIntersects = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown filter spatial operation: $value\\n");
+     }
+      }
+   }
+
+   function queryDistanceOperations() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Filter/Distance/Operation");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Within" : $this->supportsDistanceWithin = TRUE; break;
+        case "Beyond" : $this->supportsBeyond = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown filter distance operation: $value\\n");
+     }
+      }
+   }
+
+
+   // EXPRESSION
+
+   // types
+
+   var $supportsBasic = FALSE;
+   function supportsBasic() {
+      return $this->supportsBasic;
+   }
+
+   var $supportsFunction = FALSE;
+   function supportsFunction() {
+      return $this->supportsFunction;
+   }
+
+   var $supportsParameter = FALSE;
+   function supportsParameter() {
+      return $this->supportsParameter;
+   }
+
+   var $supportsAvg = FALSE;
+   function supportsAvg() {
+      return $this->supportsAvg;
+   }
+
+   var $supportsCeil = FALSE;
+   function supportsCeil() {
+      return $this->supportsCeil;
+   }
+
+   var $supportsCLIP = FALSE;
+   function supportsCLIP() {
+      return $this->supportsCLIP;
+   }
+
+   var $supportsConcat = FALSE;
+   function supportsConcat() {
+      return $this->supportsConcat;
+   }
+
+   var $supportsCount = FALSE;
+   function supportsCount() {
+      return $this->supportsCount;
+   }
+
+   var $supportsFloor = FALSE;
+   function supportsFloor() {
+      return $this->supportsFloor;
+   }
+
+   var $supportsLower = FALSE;
+   function supportsLower() {
+      return $this->supportsLower;
+   }
+
+   var $supportsMax = FALSE;
+   function supportsMax() {
+      return $this->supportsMax;
+   }
+
+   var $supportsMin = FALSE;
+   function supportsMin() {
+      return $this->supportsMin;
+   }
+
+   var $supportsMOSAIC = FALSE;
+   function supportsMOSAIC() {
+      return $this->supportsMOSAIC;
+   }
+
+   var $supportsStdDev = FALSE;
+   function supportsStdDev() {
+      return $this->supportsStdDev;
+   }
+
+   var $supportsSum = FALSE;
+   function supportsSum() {
+      return $this->supportsSum;
+   }
+
+   var $supportsUpper = FALSE;
+   function supportsUpper() {
+      return $this->supportsUpper;
+   }
+
+   function queryExpression() {
+      $this->queryExpressionType();
+      $this->queryFunctionDefinitions();
+   }
+
+   function queryExpressionType() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Expression/Type/Name");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Basic" : $this->supportsBasic = TRUE; break;
+        case "Function" : $this->supportsFunction = TRUE; break;
+        case "Parameter" : $this->supportsParameter = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown filter distance operation: $value\\n");
+     }
+      }
+   }
+
+   function queryFunctionDefinitions() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Expression/FunctionDefinitionCollection/FunctionDefinition/Name");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Avg" : $this->supportsAvg = TRUE; break;
+        case "Ceil" : $this->supportsCeil = TRUE; break;
+        case "CLIP" : $this->supportsCLIP = TRUE; break;
+        case "Concat" : $this->supportsConcat = TRUE; break;
+        case "Count" : $this->supportsCount = TRUE; break;
+        case "Floor" : $this->supportsFloor = TRUE; break;
+        case "Lower" : $this->supportsLower = TRUE; break;
+        case "Max" : $this->supportsMax = TRUE; break;
+        case "Min" : $this->supportsMin = TRUE; break;
+        case "MOSAIC" : $this->supportsMOSAIC = TRUE; break;
+        case "StdDev" : $this->supportsStdDev = TRUE; break;
+        case "Sum" : $this->supportsSum = TRUE; break;
+        case "Upper" : $this->supportsUpper = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown expression function name: $value\\n");
+     }
+      }
+   }
+
+   // RASTER
+
+   var $supportsRaster = FALSE;
+   function supportsRaster() {
+      return $this->supportsRaster;
+   }
+
+   var $supportsStitching = FALSE;
+   function supportsStitching() {
+      return $this->supportsStitching;
+   }
+
+   var $supportsSubsampling = FALSE;
+   function supportsSubsampling() {
+      return $this->supportsSubsampling;
+   }
+
+   function queryRaster() {
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Raster/SupportsRaster", $this->supportsRaster);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Raster/SupportsStitching", $this->supportsStitching);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Raster/SupportsSubsampling", $this->supportsSubsampling);
+   }
+
+   // TOPOLOGY
+
+   var $supportsTopology = FALSE;
+   function supportsTopology() {
+      return $this->supportsTopology;
+   }
+
+   var $supportsTopologicalHierarchy = FALSE;
+   function supportsTopologicalHierarchy() {
+      return $this->supportsTopologicalHierarchy;
+   }
+
+   var $supportsBreaksCurveCrossingsAutomatically = FALSE;
+   function supportsBreaksCurveCrossingsAutomatically() {
+      return $this->supportsBreaksCurveCrossingsAutomatically;
+   }
+
+   var $supportsActivatesTopologyByArea = FALSE;
+   function supportsActivatesTopologyByArea() {
+      return $this->supportsActivatesTopologyByArea;
+   }
+
+   var $supportsConstrainsFeatureMovements = FALSE;
+   function supportsConstrainsFeatureMovements() {
+      return $this->supportsConstrainsFeatureMovements;
+   }
+
+   function queryTopology() {
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Topology/SupportsTopology", $this->supportsTopology);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Topology/SupportsTopologicalHierarchy", $this->supportsTopologicalHierarchy);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Topology/BreaksCurveCrossingsAutomatically", $this->supportsBreaksCurveCrossingsAutomatically);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Topology/ActivatesTopologyByArea", $this->supportsActivatesTopologyByArea);
+      $this->queryBooleanElement("//FeatureProviderCapabilities/Topology/ConstrainsFeatureMovements", $this->supportsConstrainsFeatureMovements);
+   }
+
+   // GEOMETRY
+
+   var $supportsPoint = FALSE;
+   function supportsPoint() {
+      return $this->supportsPoint;
+   }
+
+   var $supportsMultiPoint = FALSE;
+   function supportsMultiPoint() {
+      return $this->supportsMultiPoint;
+   }
+
+   var $supportsLineString = FALSE;
+   function supportsLineString() {
+      return $this->supportsLineString;
+   }
+
+   var $supportsMultiLineString = FALSE;
+   function supportsMultiLineString() {
+      return $this->supportsMultiLineString;
+   }
+
+   var $supportsPolygon = FALSE;
+   function supportsPolygon() {
+      return $this->supportsPolygon;
+   }
+
+   var $supportsMultiPolygon = FALSE;
+   function supportsMultiPolygon() {
+      return $this->supportsMultiPolygon;
+   }
+
+   var $supportsCurveString = FALSE;
+   function supportsCurveString() {
+      return $this->supportsCurveString;
+   }
+
+   var $supportsMultiCurveString = FALSE;
+   function supportsMultiCurveString() {
+      return $this->supportsMultiCurveString;
+   }
+
+   var $supportsCurvePolygon = FALSE;
+   function supportsCurvePolygon() {
+      return $this->supportsCurvePolygon;
+   }
+
+   var $supportsMultiCurvePolygon = FALSE;
+   function supportsMultiCurvePolygon() {
+      return $this->supportsMultiCurvePolygon;
+   }
+
+   var $supportsMultiGeometry = FALSE;
+   function supportsMultiGeometry() {
+      return $this->supportsMultiGeometry;
+   }
+
+   var $supportsLinearRing = FALSE;
+   function supportsLinearRing() {
+      return $this->supportsLinearRing;
+   }
+   var $supportsLinearSegment = FALSE;
+   function supportsLinearSegment() {
+      return $this->supportsLinearSegment;
+   }
+   var $supportsArcSegment = FALSE;
+   function supportsArcSegment() {
+      return $this->supportsArcSegment;
+   }
+   var $supportsCurveRing = FALSE;
+   function supportsCurveRing() {
+      return $this->supportsCurveRing;
+   }
+
+   var $dimensionality = -1;
+   function getDimensionality() {
+      return $this->dimensionality;
+   }
+
+   function queryGeometry() {
+      $this->queryGeometries();
+      $this->queryComponents();
+      $this->queryDimensionality();
+   }
+
+   function queryGeometries() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Geometry/Type");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "Point" : $this->supportsPoint = TRUE; break;
+        case "MultiPoint" : $this->supportsMultiPoint = TRUE; break;
+        case "LineString" : $this->supportsLineString = TRUE; break;
+        case "MultiLineString" : $this->supportsMultiLineString = TRUE; break;
+        case "Polygon" : $this->supportsPolygon = TRUE; break;
+        case "MultiPolygon" : $this->supportsMultiPolygon = TRUE; break;
+        case "CurveString" : $this->supportsCurveString = TRUE; break;
+        case "MultiCurveString" : $this->supportsMultiCurveString = TRUE; break;
+        case "CurvePolygon" : $this->supportsCurvePolygon = TRUE; break;
+        case "MultiCurvePolygon" : $this->supportsMultiCurvePolygon = TRUE; break;
+        case "MultiGeometry" : $this->supportsMultiGeometry = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown geometry type: $value\\n");
+     }
+      }
+   }
+
+   function queryComponents() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Geometry/Components/Type");
+      $count = $domNodeList->length;
+      for($i=0; $i<$count; $i++) {
+     $domNode = $domNodeList->item($i);
+     $value = $domNode->nodeValue;
+     switch($value) {
+        case "LinearRing" : $this->supportsLinearRing = TRUE; break;
+        case "LinearSegment" : $this->supportsLinearSegment = TRUE; break;
+        case "ArcSegment" : $this->supportsArcSegment = TRUE; break;
+        case "CurveRing" : $this->supportsCurveRing = TRUE; break;
+        default : fwrite($this->logFileHandle, "unknown geometry component type: $value\\n");
+     }
+      }
+   }
+
+   function queryDimensionality() {
+      $domNodeList = $this->xpath->query("//FeatureProviderCapabilities/Geometry/Dimensionality");
+      if ($domNodeList->length == 0) return;
+      $domNode = $domNodeList->item(0);
+      $value = $domNode->nodeValue;
+      $this->dimensionality = $value;
+   }
+
+   function logCapabilities() {
+      fwrite($this->logFileHandle, "Capabilities for $this->providerName\\n");
+      fwrite($this->logFileHandle, "Connection\\n");
+      fwrite($this->logFileHandle, "\\tThreadCapability\\n");
+      $logEntry = "\\t\\tsupportsSingleThreaded(): " . $this->prtBool($this->supportsSingleThreaded()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsPerConnectionThreaded(): " . $this->prtBool($this->supportsPerConnectionThreaded()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsPerCommandThreaded(): " . $this->prtBool($this->supportsPerCommandThreaded()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsMultiThreaded(): " . $this->prtBool($this->supportsMultiThreaded()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "\\tSpatialContextExtent\\n");
+      fwrite($this->logFileHandle, "\\t\\tType\\n");
+      $logEntry = "\\t\\t\\tsupportsDynamic(): " . $this->prtBool($this->supportsDynamic()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsStatic(): " . $this->prtBool($this->supportsStatic()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsLocking(): " . $this->prtBool($this->supportsLocking()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsConnectionTimeout(): " . $this->prtBool($this->supportsConnectionTimeout()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsTransactions(): " . $this->prtBool($this->supportsTransactions()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsLongTransactions(): " . $this->prtBool($this->supportsLongTransactions()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSQL(): " . $this->prtBool($this->supportsSQL()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsConfiguration(): " . $this->prtBool($this->supportsConfiguration()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "Schema\\n");
+      fwrite($this->logFileHandle, "\\tClass\\n");
+      fwrite($this->logFileHandle, "\\t\\tType\\n");
+     $logEntry = "\\t\\t\\tsupportsClass(): " . $this->prtBool($this->supportsClass()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsFeatureClass(): " . $this->prtBool($this->supportsFeatureClass()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "\\tData\\n");
+      fwrite($this->logFileHandle, "\\t\\tType\\n");
+      $logEntry = "\\t\\t\\tsupportsBoolean(): " . $this->prtBool($this->supportsBoolean()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsByte(): " . $this->prtBool($this->supportsByte()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDateTime(): " . $this->prtBool($this->supportsDateTime()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDecimal(): " . $this->prtBool($this->supportsDecimal()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDouble(): " . $this->prtBool($this->supportsDouble()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsInt16(): " . $this->prtBool($this->supportsInt16()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsInt32(): " . $this->prtBool($this->supportsInt32()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsInt64(): " . $this->prtBool($this->supportsInt64()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsSingle(): " . $this->prtBool($this->supportsSingle()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsString(): " . $this->prtBool($this->supportsString()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsBLOB(): " . $this->prtBool($this->supportsBLOB()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCLOB(): " . $this->prtBool($this->supportsCLOB()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsUniqueID(): " . $this->prtBool($this->supportsUniqueID()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsInheritance(): " . $this->prtBool($this->supportsInheritance()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsMultipleSchemas(): " . $this->prtBool($this->supportsMultipleSchemas()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsObjectProperties(): " . $this->prtBool($this->supportsObjectProperties()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsAssociationProperties(): " . $this->prtBool($this->supportsAssociationProperties()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSchemaOverrides(): " . $this->prtBool($this->supportsSchemaOverrides()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsNetworkModel(): " . $this->prtBool($this->supportsNetworkModel()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsAutoIdGeneration(): " . $this->prtBool($this->supportsAutoIdGeneration()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsDataStoreScopeUniqueIdGeneration(): " . $this->prtBool($this->supportsDataStoreScopeUniqueIdGeneration()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSchemaModification(): " . $this->prtBool($this->supportsSchemaModification()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "Command\\n");
+      fwrite($this->logFileHandle, "\\tSupportedCommands\\n");
+      fwrite($this->logFileHandle, "\\t\\tName\\n");
+      $logEntry = "\\t\\t\\tsupportsSelect(): " . $this->prtBool($this->supportsSelect()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsInsert(): " . $this->prtBool($this->supportsInsert()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDelete(): " . $this->prtBool($this->supportsDelete()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsUpdate(): " . $this->prtBool($this->supportsUpdate()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsSelectAggregates(): " . $this->prtBool($this->supportsSelectAggregates()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDescribeSchema(): " . $this->prtBool($this->supportsDescribeSchema()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDescribeSchemaMapping(): " . $this->prtBool($this->supportsDescribeSchemaMapping()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDestroySchema(): " . $this->prtBool($this->supportsDestroySchema()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsApplySchema(): " . $this->prtBool($this->supportsApplySchema()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsActivateSpatialContext(): " . $this->prtBool($this->supportsActivateSpatialContext()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCreateSpatialContext(): " . $this->prtBool($this->supportsCreateSpatialContext()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDestroySpatialContext(): " . $this->prtBool($this->supportsDestroySpatialContext()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDestroySpatialContext(): " . $this->prtBool($this->supportsGetSpatialContexts) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCreateMeasureUnit(): " . $this->prtBool($this->supportsCreateMeasureUnit()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDestroyMeasureUnit()t: " . $this->prtBool($this->supportsDestroyMeasureUnit()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetMeasureUnits(): " . $this->prtBool($this->supportsGetMeasureUnits()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsSQLCommand(): " . $this->prtBool($this->supportsSQLCommand()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsAcquireLock(): " . $this->prtBool($this->supportsAcquireLock()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetLockInfo(): " . $this->prtBool($this->supportsGetLockInfo()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetLockedObjects(): " . $this->prtBool($this->supportsGetLockedObjects()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetLockOwners(): " . $this->prtBool($this->supportsGetLockOwners()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsReleaseLock(): " . $this->prtBool($this->supportsReleaseLock()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsActivateLongTransaction(): " . $this->prtBool($this->supportsActivateLongTransaction()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCommitLongTransaction(): " . $this->prtBool($this->supportsCommitLongTransaction()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCreateLongTransaction(): " . $this->prtBool($this->supportsCreateLongTransaction()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetLongTransactions(): " . $this->prtBool($this->supportsGetLongTransactions()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsFreezeLongTransaction(): " . $this->prtBool($this->supportsFreezeLongTransaction()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsRollbackLongTransaction(): " . $this->prtBool($this->supportsRollbackLongTransaction()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsActivateLongTransactionCheckpoint(): " . $this->prtBool($this->supportsActivateLongTransactionCheckpoint()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCreateLongTransactionCheckpoint(): " . $this->prtBool($this->supportsCreateLongTransactionCheckpoint()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetLongTransactionCheckpoints(): " . $this->prtBool($this->supportsGetLongTransactionCheckpoints()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsRollbackLongTransactionCheckpoint(): " . $this->prtBool($this->supportsRollbackLongTransactionCheckpoint()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsChangeLongTransactionPrivileges(): " . $this->prtBool($this->supportsChangeLongTransactionPrivileges()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetLongTransactionPrivileges(): " . $this->prtBool($this->supportsGetLongTransactionPrivileges()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsChangeLongTransactionSet(): " . $this->prtBool($this->supportsChangeLongTransactionSet()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsGetLongTransactionsInSet(): " . $this->prtBool($this->supportsGetLongTransactionsInSet()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsFirstProviderCommand(): " . $this->prtBool($this->supportsFirstProviderCommand()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDeactivateLongTransaction(): " . $this->prtBool($this->supportsDeactivateLongTransaction()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsParameters(): " . $this->prtBool($this->supportsParameters()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsCommandTimeout(): " . $this->prtBool($this->supportsCommandTimeout()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSelectExpressions(): " . $this->prtBool($this->supportsSelectExpressions()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSelectFunctions(): " . $this->prtBool($this->supportsSelectFunctions()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSelectDistinct(): " . $this->prtBool($this->supportsSelectDistinct()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSelectOrdering(): " . $this->prtBool($this->supportsSelectOrdering()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSelectGrouping(): " . $this->prtBool($this->supportsSelectGrouping()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "Filter\\n");
+      fwrite($this->logFileHandle, "\\tCondition\\n");
+      fwrite($this->logFileHandle, "\\t\\tType\\n");
+      $logEntry = "\\t\\t\\tsupportsComparison(): " . $this->prtBool($this->supportsComparison()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsLike(): " . $this->prtBool($this->supportsLike()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsIn(): " . $this->prtBool($this->supportsIn()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsNull(): " . $this->prtBool($this->supportsNull()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsSpatial(): " . $this->prtBool($this->supportsSpatial()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDistance(): " . $this->prtBool($this->supportsDistance()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "\\tSpatial\\n");
+      fwrite($this->logFileHandle, "\\t\\tOperation\\n");
+      $logEntry = "\\t\\t\\tsupportsContains(): " . $this->prtBool($this->supportsContains()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCrosses(): " . $this->prtBool($this->supportsCrosses()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsDisjoint(): " . $this->prtBool($this->supportsDisjoint()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsEquals(): " . $this->prtBool($this->supportsEquals()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsIntersects(): " . $this->prtBool($this->supportsIntersects()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsOverlaps()s: " . $this->prtBool($this->supportsOverlaps()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsTouches(): " . $this->prtBool($this->supportsTouches()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsSpatialWithin(): " . $this->prtBool($this->supportsSpatialWithin()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCoveredBy(): " . $this->prtBool($this->supportsCoveredBy()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsInside(): " . $this->prtBool($this->supportsInside()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsEnvelopeIntersects(): " . $this->prtBool($this->supportsEnvelopeIntersects()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "\\tDistance\\n");
+      fwrite($this->logFileHandle, "\\t\\tOperation\\n");
+      $logEntry = "\\t\\t\\tsupportsDistanceWithin(): " . $this->prtBool($this->supportsDistanceWithin()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsBeyond(): " . $this->prtBool($this->supportsBeyond()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsGeodesicDistance(): " . $this->prtBool($this->supportsGeodesicDistance()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsNonLiteralGeometricOperations(): " . $this->prtBool($this->supportsNonLiteralGeometricOperations()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "Expression\\n");
+      fwrite($this->logFileHandle, "\\tType\\n");
+      fwrite($this->logFileHandle, "\\t\\tName\\n");
+      $logEntry = "\\t\\t\\tsupportsBasic(): " . $this->prtBool($this->supportsBasic()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsFunction(): " . $this->prtBool($this->supportsFunction()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsParameter(): " . $this->prtBool($this->supportsParameter()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "\\tFunctionDefinitionCollection\\n");
+      fwrite($this->logFileHandle, "\\t\\tFunctionDefinition\\n");
+      fwrite($this->logFileHandle, "\\t\\t\\tName\\n");
+      $logEntry = "\\t\\t\\t\\tsupportsAvg(): " . $this->prtBool($this->supportsAvg()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsCeil(): " . $this->prtBool($this->supportsCeil()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsCLIP(): " . $this->prtBool($this->supportsCLIP()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsConcat(): " . $this->prtBool($this->supportsConcat()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsCount(): " . $this->prtBool($this->supportsCount()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsFloor(): " . $this->prtBool($this->supportsFloor()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsLower(): " . $this->prtBool($this->supportsLower()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsMax(): " . $this->prtBool($this->supportsMax()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsMin(): " . $this->prtBool($this->supportsMin()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsMOSAIC(): " . $this->prtBool($this->supportsMOSAIC()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsStdDev(): " . $this->prtBool($this->supportsStdDev()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsSum(): " . $this->prtBool($this->supportsSum()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\t\\tsupportsUpper(): " . $this->prtBool($this->supportsUpper()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "Raster\\n");
+      $logEntry = "\\tsupportsRaster(): " . $this->prtBool($this->supportsRaster()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsStitching(): " . $this->prtBool($this->supportsStitching()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsSubsampling(): " . $this->prtBool($this->supportsSubsampling()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "Topology\\n");
+      $logEntry = "\\tsupportsTopology(): " . $this->prtBool($this->supportsTopology()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsTopologicalHierarchy(): " . $this->prtBool($this->supportsTopologicalHierarchy()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsBreaksCurveCrossingsAutomatically(): " . $this->prtBool($this->supportsBreaksCurveCrossingsAutomatically()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsActivatesTopologyByArea(): " . $this->prtBool($this->supportsActivatesTopologyByArea()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tsupportsConstrainsFeatureMovements(): " . $this->prtBool($this->supportsConstrainsFeatureMovements()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "Geometry\\n");
+      fwrite($this->logFileHandle, "\\tType\\n");
+      $logEntry = "\\t\\tsupportsPoint(): " . $this->prtBool($this->supportsPoint()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsMultiPoint(): " . $this->prtBool($this->supportsMultiPoint()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsLineString(): " . $this->prtBool($this->supportsLineString()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsMultiLineString(): " . $this->prtBool($this->supportsMultiLineString()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsPolygon(): " . $this->prtBool($this->supportsPolygon()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsMultiPolygon(): " . $this->prtBool($this->supportsMultiPolygon()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsCurveString(): " . $this->prtBool($this->supportsCurveString()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsMultiCurveString(): " . $this->prtBool($this->supportsMultiCurveString()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsCurvePolygon(): " . $this->prtBool($this->supportsCurvePolygon()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsMultiCurvePolygon(): " . $this->prtBool($this->supportsMultiCurvePolygon()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\tsupportsMultiGeometry(): " . $this->prtBool($this->supportsMultiGeometry()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      fwrite($this->logFileHandle, "\\tComponents\\n");
+      fwrite($this->logFileHandle, "\\t\\tType\\n");
+      $logEntry = "\\t\\t\\tsupportsLinearRing(): " . $this->prtBool($this->supportsLinearRing()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsLinearSegment(): " . $this->prtBool($this->supportsLinearSegment()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsArcSegment(): " . $this->prtBool($this->supportsArcSegment()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\t\\t\\tsupportsCurveRing(): " . $this->prtBool($this->supportsCurveRing()) . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+      $logEntry = "\\tdimensionality(): " . $this->getDimensionality() . "\\n";
+      fwrite($this->logFileHandle, $logEntry);
+   }
+}
+```
+

Added: branches/4.0/MgDev/Doc/landing/topics/feature-schema.md.tpl
===================================================================
--- branches/4.0/MgDev/Doc/landing/topics/feature-schema.md.tpl	                        (rev 0)
+++ branches/4.0/MgDev/Doc/landing/topics/feature-schema.md.tpl	2023-11-10 20:21:36 UTC (rev 10068)
@@ -0,0 +1,154 @@
+# Feature Schema
+
+A feature schema is a class or set of classes that define the
+structure of data contained in a feature source (datastore),
+for example, an Oracle database. A feature class consists of
+a set of properties. Each property has a set of attributes
+appropriate to the type of information it contains. Class and
+property here correspond roughly to table and column in a
+relational database.
+
+The class and property definition classes have methods for
+both getting and setting attributes. In general, however, the
+set methods are of no use except in the case of the FDO
+Provider for SDF. `MgFeatureService::CreateFeatureSource`
+permits you to create an SDF schema, that is, an SDF file
+containing a schema. The intent is that this method be used
+as part of a mechanism for caching feature data locally.
+The schema used for this SDF cache file would typically be
+the schema extracted by `MgFeatureService::DescribeSchema`
+from the feature source whose features are being cached.
+It is possible to create a schema from scratch; see the sample code below.
+
+Five property types are distinguished, and there is a
+property definition class for each type. The five types are:
+
+ * Simple data. The MgDataPropertyDefinition [!doclinkclass MgDataPropertyDefinition] class is used
+to define the attributes of simple data properties. The
+simple data types are BLOB, boolean, byte, CLOB, MgDateTime,
+double, Int16, Int32, Int64, single, and string.
+ * Geometry data. The MgGeometricPropertyDefinition [!doclinkclass MgGeometricPropertyDefinition] class is
+used to define the attributes of geometry data. The geometry
+data type is MgGeometry.
+ * Association data. The MgAssociationPropertyDefinition [!doclinkclass MgAssociationPropertyDefinition]
+class is used to define the attributes of association
+properties. This is not currently used.
+ * Object data. The MgObjectPropertyDefinition [!doclinkclass MgObjectPropertyDefinition] class is used
+to define the attributes of object data. This allows us to
+model information using object composition. The type of
+object data is user-defined.
+ * Raster data. The MgRasterPropertyDefinition [!doclinkclass MgRasterPropertyDefinition] class is used
+to define the attributes of raster data. The type of raster
+data is MgRaster [!doclinkclass MgRaster]. 
+
+```C#
+using OSGeo.MapGuide;
+MgDataPropertyDefinition dataProperty;
+MgGeometricPropertyDefinition geometricProperty;
+MgClassDefinition classDef = new MgClassDefinition();
+
+className = "SdfFeatureClass";
+
+classDef.SetName(className);
+classDef.SetDefaultGeometryPropertyName("theFeatureGeometry");
+
+// feature geometry property that takes 2D XY geometries
+geometricProperty = new MgGeometricPropertyDefinition("theFeatureGeometry");
+geometricProperty.SetGeometryTypes(MgFeatureGeometricType.Point |
+   MgFeatureGeometricType.Curve | MgFeatureGeometricType.Surface);
+geometricProperty.SetHasElevation(false);
+geometricProperty.SetHasMeasure(false);
+geometricProperty.SetReadOnly(false);
+geometricProperty.SetSpatialContextAssociation(spatialContextName);
+classDef.GetProperties().Add(geometricProperty);
+
+// non-feature geometry
+geometricProperty = new MgGeometricPropertyDefinition("aNonFeatureGeometry");
+geometricProperty.SetGeometryTypes(MgFeatureGeometricType.Point |
+  MgFeatureGeometricType.Curve | MgFeatureGeometricType.Surface);
+geometricProperty.SetHasElevation(false);
+geometricProperty.SetHasMeasure(false);
+geometricProperty.SetReadOnly(false);
+geometricProperty.SetSpatialContextAssociation(spatialContextName);
+classDef.GetProperties().Add(geometricProperty);
+
+// identity property
+dataProperty = new MgDataPropertyDefinition("FeatId");
+dataProperty.SetDataType(MgPropertyType.Int32);
+dataProperty.SetAutoGeneration(false);
+dataProperty.SetNullable(false);
+dataProperty.SetReadOnly(true);
+classDef.GetIdentityProperties().Add(dataProperty);
+classDef.GetProperties().Add(dataProperty);
+
+// boolean property
+dataProperty = new MgDataPropertyDefinition("aBoolean");
+dataProperty.SetDataType(MgPropertyType.Boolean);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// byte property
+dataProperty = new MgDataPropertyDefinition("aByte");
+dataProperty.SetDataType(MgPropertyType.Byte);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// DataTime property
+dataProperty = new MgDataPropertyDefinition("aDateTime");
+dataProperty.SetDataType(MgPropertyType.DateTime);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// cannot create Decimal property because MgPropertyType.Decimal doesn't exist
+// even though SDF schema capabilities says that Decimal property is supported
+
+// double property
+dataProperty = new MgDataPropertyDefinition("aDouble");
+dataProperty.SetDataType(MgPropertyType.Double);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// Int16 property
+dataProperty = new MgDataPropertyDefinition("anInt16");
+dataProperty.SetDataType(MgPropertyType.Int16);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// Int32 property
+dataProperty = new MgDataPropertyDefinition("anInt32");
+dataProperty.SetDataType(MgPropertyType.Int32);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// Int64 property
+dataProperty = new MgDataPropertyDefinition("anInt64");
+dataProperty.SetDataType(MgPropertyType.Int64);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// Single property
+dataProperty = new MgDataPropertyDefinition("aSingle");
+dataProperty.SetDataType(MgPropertyType.Single);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+// String property
+dataProperty = new MgDataPropertyDefinition("aString");
+dataProperty.SetDataType(MgPropertyType.String);
+dataProperty.SetNullable(true);
+dataProperty.SetReadOnly(false);
+classDef.GetProperties().Add(dataProperty);
+
+schema = new MgFeatureSchema();
+schemaName = "SdfFeatureClassSchema";
+schema.SetName(schemaName);
+schema.GetClasses().Add(classDef);
+```
\ No newline at end of file

Modified: branches/4.0/MgDev/Doc/landing/topics/toc.yml
===================================================================
--- branches/4.0/MgDev/Doc/landing/topics/toc.yml	2023-11-10 19:15:59 UTC (rev 10067)
+++ branches/4.0/MgDev/Doc/landing/topics/toc.yml	2023-11-10 20:21:36 UTC (rev 10068)
@@ -7,8 +7,21 @@
   href: collections.md
 - name: Feature Service
   items:
+    - name: Introduction
+      href: feature-intro.md
+    - name: Feature Providers
+      href: feature-provider.md
+    - name: Feature Provider Registry
+      href: feature-provider-registry.md
+    - name: Connection to Feature Source
+      href: feature-connection.md
+    - name: Feature Schemas
+      href: feature-schema.md
     - name: Feature Properties
       href: feature-properties.md
+    - name: Filters and Expressions
+      href: feature-filters.md
+
 - name: Geometry
   items:
     - name: Collections



More information about the mapguide-commits mailing list