Analyzing Features

Introduction

Note

The Analyzing Features sample, in the Developer’s Guide samples, demonstrates concepts from this chapter.

MapGuide includes methods for analyzing map features, including comparing the spatial relationships between features, measuring distances, and creating buffer areas around features.

Analyzing features requires knowing how the features are represented and what spatial reference systems are being used. If different spatial reference systems are being used, it is important to be able to convert between them.

Representations of Geometry

MapGuide can represent geometric data in three different forms:

  • AGF text format, which is an extension of the Open Geospatial Consortium (OGC) Well Known Text (WKT) format. This is used to represent geometry as a character string.
  • Binary AGF format. This is used by the FDO technology supporting the Feature Service.
  • MapGuide internal representation, using MgGeometry and classes derived from it.

Note

This guide and the Web API Reference will often use the term WKT to mean AGF text format. Be aware that AGF Text values do not always conform to the OGC WKT standard. See the Geometry module in the Web API Reference for details.

To convert between AGF text and the MapGuide internal representation, use an MgWktReaderWriter object. Call MgWktReaderWriter.Read() to convert AGF text to MgGeometry. Call MgWktReaderWriter.Write() to convert MgGeometry to AGF text.

To convert between binary AGF and the MapGuide internal representation, use an MgAgfReaderWriter object. Call MgAgfReaderWriter.Read() to convert binary AGF to MgGeometry. Call MgAgfReaderWriter.Write() to convert MgGeometry to binary AGF.

For example, if you have a WKT representation of the geometry, you could create a geometry object as follows:

PHP

$wktReaderWriter = new MgWktReaderWriter();
$geometry = wktReaderWriter->Read($wktGeometry);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgWktReaderWriter wktReaderWriter = new MgWktReaderWriter();
MgGeometry geometry = wktReaderWriter.Read(wktGeometry);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgWktReaderWriter wktReaderWriter = new MgWktReaderWriter();
MgGeometry geometry = wktReaderWriter.Read(wktGeometry);

Geometry Objects

MgGeometry is the base class for all the geometry types. The simple geometry types are:

  • MgPoint - a single point
  • MgLineString - a series of connected line segments
  • MgCurveString - a series of connected curve segments
  • MgPolygon - a polygon with sides formed from line segments
  • MgCurvePolygon - a polygon with sides formed from curve segments

The curve segments are circular arcs, defined by a start point, an end point, and a control point.

Complex types are formed by aggregating simple types. The complex types are:

  • MgMultiPoint - a group of points
  • MgMultiLineString - a group of line strings
  • MgMultiCurveString - a group of curve strings
  • MgMultiPolygon - a group of polygons
  • MgMultiCurvePolygon - a group of curve polygons
  • MgMultiGeometry - a group of simple geometry objects of any type

Comparing Geometry Objects

The MgGeometry class contains methods for comparing different geometry objects. These are similar to the spatial filters described in Selecting with the Web API. Methods to test spatial relationships include:

  • Contains()
  • Crosses()
  • Disjoint()
  • Equals()
  • Intersects()
  • Overlaps()
  • Touches()
  • Within()

For example, if you have an MgLineString object $line and an MgPolygon object $polygon, you can test if the line crosses the polygon with a call to

PHP

$line->Crosses($polygon)

.net (C#)

line.Crosses(polygon)

Java

line.Crosses(polygon)

Methods to create new geometry objects from the point set of two other geometries include:

  • Difference()
  • Intersection()
  • SymmetricDifference()
  • Union()

Complete details are in the Geometry module of the Web API reference, under Spatial Relationships.

Coordinate Systems

A single map will often combine data from different sources, and the different sources may use different coordinate systems. The map has its own coordinate system, and any feature sources used in the map may have different coordinate systems. It is important for display and analysis that all locations are transformed to the same coordinate system.

Note

A coordinate system can also be called a spatial reference system (SRS) or a coordinate reference system (CRS). This guide uses the abbreviation SRS.

MapGuide supports three different types of coordinate system:

  • Arbitrary X-Y
  • Geographic, or latitude/longitude
  • Projected

An MgCoordinateSystem object represents a coordinate system.

Note

You cannot transform between arbitrary X-Y coordinates and either geographic or projected coordinates.

To create an MgCoordinateSystem object from an MgMap object,

  • Get the WKT representation of the map coordinate system, using MgMap::GetMapSRS().
  • Create an MgCoordinateSystem object, using MgCoordinateSystemFactory::Create().

To create an MgCoordinateSystem object from a map layer,

  • Get the feature source for the layer.
  • Get the active spatial context for the feature source.
  • Convert the spatial context to a WKT.
  • Create an MgCoordinateSystem object from the WKT.

To transform geometry from one coordinate system to another, create an MgCoordinateSystemTransform object using the two coordinate systems. Apply this transform to the MgGeometry object.

For example, if you have geometry representing a feature on a layer that uses one coordinate system, and you want to compare it to a feature on another layer that uses a different coordinate system, perform the following steps:

PHP

$featureSource1 = $layer1->GetFeatureSourceId();
$contexts1 = $featureService->GetSpatialContexts($featureSource1, true);
$contexts1->ReadNext();
$srs1 = $contexts1->GetCoordinateSystemWkt();
$contexts1->Close();
$featureSource2 = $layer2->GetFeatureSourceId();
$contexts2 = $featureService->GetSpatialContexts($featureSource2, true);
$contexts2->ReadNext();
$srs2 = $contexts2->GetCoordinateSystemWkt();
$contexts2->Close();
$csFactory = new MgCoordinateSystemFactory();
$srcCs = $csFactory->Create($srs1);
$dstCs = $csFactory->Create($srs2);
$xform = $csFactory->GetTransform($srcCS, $dstCs);
$geometry1xform = $geometry1->Transform($xform);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgResourceIdentifier featureSource1 = layer1.GetFeatureSourceId();
MgSpatialContextReader contexts1 = featureService.GetSpatialContexts(featureSource1, true);
contexts1.ReadNext();
String srs1 = contexts1.GetCoordinateSystemWkt();
contexts1.Close();
MgResourceIdentifier featureSource2 = layer2.GetFeatureSourceId();
MgSpatialContextReader contexts2 = featureService.GetSpatialContexts(featureSource2, true);
contexts2.ReadNext();
String srs2 = contexts2.GetCoordinateSystemWkt();
contexts2.Close();
MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
MgCoordinateSystem srcCs = csFactory.Create(srs1);
MgCoordinateSystem dstCs = csFactory.Create(srs2);
MgCoordianteSystemTransform xform = csFactory.GetTransform(srcCs, dstCs);
MgGeometry geometry1xform = geometry1.Transform(xform);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgResourceIdentifier featureSource1 = layer1.GetFeatureSourceId();
MgSpatialContextReader contexts1 = featureService.GetSpatialContexts(featureSource1, true);
contexts1.ReadNext();
String srs1 = contexts1.GetCoordinateSystemWkt();
contexts1.Close();
MgResourceIdentifier featureSource2 = layer2.GetFeatureSourceId();
MgSpatialContextReader contexts2 = featureService.GetSpatialContexts(featureSource2, true);
contexts2.ReadNext();
String srs2 = contexts2.GetCoordinateSystemWkt();
contexts2.Close();
MgCoordinateSystemFactory csFactory = new MgCoordinateSystemFactory();
MgCoordinateSystem srcCs = csFactory.Create(srs1);
MgCoordinateSystem dstCs = csFactory.Create(srs2);
MgCoordianteSystemTransform xform = csFactory.GetTransform(srcCs, dstCs);
MgGeometry geometry1xform = geometry1.Transform(xform);

Measuring Distance

Measuring distance in geographic or projected coordinate systems requires great circle calculations. Both MgGeometry::Buffer() and MgGeometry::Distance() accept a measurement parameter that defines the great circle to be used. If the measurement parameter is null, the calculation is done using a linear algorithm.

Create the measurement parameter, an MgCoordinateSystemMeasure object, from the MgCoordinateSystem object.

Distance is calculated in the units of the SRS. MgCoordinateSystem includes two methods, ConvertCoordinateSystemUnitsToMeters() and ConvertMetersToCoordinateSystemUnits() to convert to and from linear distances.

For example, to calculate the distance between two MgGeometry objects $a and $b, using the coordinate system $srs, perform the following steps:

PHP

$measure = $srs->GetMeasure();
$distInMapUnits = $a->Distance($b, $measure);
$distInMeters = $srs->ConvertCoordinateSystemUnitsToMeters($distInMapUnits);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgCoordinateSystemMeasure measure = srs.GetMeasure();
double distInMapUnits = a.Distance(b, measure);
double distInMeters = srs.ConvertCoordinateSystemUnitsToMeters(distInMapUnits);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgCoordinateSystemMeasure measure = srs.GetMeasure();
double distInMapUnits = a.Distance(b, measure);
double distInMeters = srs.ConvertCoordinateSystemUnitsToMeters(distInMapUnits);

Another way to calculate the distance is to use MgCoordinateSystemMeasure::GetDistance(), as in the following:

PHP

$distInMapUnits = $measure->GetDistance($a, $b);

.net (C#)

double distInMapUnits = measure.GetDistance(a, b);

Java

double distInMapUnits = measure.GetDistance(a, b);

Temporary Feature Sources

Many geometric analysis operations require creating new features and new feature sources. For example, drawing a buffer around a point on a map requires a layer to display the buffer polygon, and the layer requires a feature source.

To create a temporary feature source, perform the following steps:

  • Create a feature class definition.
  • Determine what properties you need to store for the features. Add the property definitions to the feature class definition.
  • Create a feature schema containing the feature class definition.
  • Determine the SRS for the feature source. This can be the same as the SRS used for the map.
  • Create a feature source using the schema and the SRS. The feature source can be stored in the session repository.

It is possible for a single feature source to contain more than one feature class. A feature source that is to be used for temporary data, however, normally contains one feature class.

A feature schema (MgFeatureSchema object) contains class definitions (MgClassDefinition objects) for each feature class in the schema.

Each class definition contains property definitions for each property in the feature class. The property definitions can be the following types:

  • MgDataPropertyDefinition
  • MgGeometryPropertyDefinition
  • MgObjectPropertyDefinition
  • MgRasterPropertyDefinition

Note

Unlike FDO, MapGuide does not currently support Association Properties

MgDataPropertyDefinition is used to define simple properties like numbers or strings. MgGeometryPropertyDefinition is used to define geometric properties. Most feature classes will have a geometric property to describe the feature’s location.

For example, the following creates a temporary feature source to hold buffer features. The feature source contains a single feature class named BufferClass.

Features in BufferClass have two properties. ID is an autogenerated unique ID number, and BufferGeometry contains the geometry for the buffer polygon.

The FDO technology supporting the Feature Service allows for multiple spatial reference systems within a single feature source. However, this capability is dependent on the data provider, and does not apply to the SDF provider that is used for creating feature sources within MapGuide. For temporary feature sources, you must define a single default SRS for the feature source, and you must set any geometry properties to use the same SRS. The name of the SRS is user-defined.

PHP

$bufferClass = new MgClassDefinition();
$bufferClass->SetName('BufferClass');
$properties = $bufferClass->GetProperties();

$idProperty = new MgDataPropertyDefinition('ID');
$idProperty->SetDataType(MgPropertyType::Int32);
$idProperty->SetReadOnly(true);
$idProperty->SetNullable(false);
$idProperty->SetAutoGeneration(true);
$properties->Add($idProperty);

$polygonProperty = new MgGeometricPropertyDefinition('BufferGeometry');
$polygonProperty->SetGeometryTypes(MgFeatureGeometricType::Surface);
$polygonProperty->SetHasElevation(false);
$polygonProperty->SetHasMeasure(false);
$polygonProperty->SetReadOnly(false);
$polygonProperty->SetSpatialContextAssociation('defaultSrs');
$properties->Add($polygonProperty);

$idProperties = $bufferClass->GetIdentityProperties();
$idProperties->Add($idProperty);

$bufferClass->SetDefaultGeometryPropertyName('BufferGeometry');
$bufferSchema = new MgFeatureSchema('BufferLayerSchema', 'temporary schema to hold a buffer');
$bufferSchema->GetClasses()->Add($bufferClass);
$sdfParams = new MgFileFeatureSourceParams('OSGeo.SDF', 'defaultSrs', $wkt, $bufferSchema);
$featureService->CreateFeatureSource($bufferFeatureResId, $sdfParams);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgClassDefinition bufferClass = new MgClassDefinition();
bufferClass.SetName("BufferClass");
MgPropertyDefinitionCollection properties = bufferClass.GetProperties();

MgDataPropertyDefinition idProperty = new MgDataPropertyDefinition("ID");
idProperty.SetDataType(MgPropertyType.Int32);
idProperty.SetReadOnly(true);
idProperty.SetNullable(false);
idProperty.SetAutoGeneration(true);
properties.Add(idProperty);

MgGeometricPropertyDefinition polygonProperty = new MgGeometricPropertyDefinition("BufferGeometry");
polygonProperty.SetGeometryTypes(MgFeatureGeometricType.Surface);
polygonProperty.SetHasElevation(false);
polygonProperty.SetHasMeasure(false);
polygonProperty.SetReadOnly(false);
polygonProperty.SetSpatialContextAssociation("defaultSrs");
properties.Add(polygonProperty);

MgPropertyDefinitionCollection idProperties = bufferClass.GetIdentityProperties();
idProperties.Add(idProperty);

bufferClass.SetDefaultGeometryPropertyName("BufferGeometry");
MgFeatureSchema bufferSchema = new MgFeatureSchema("BufferLayerSchema", "temporary schema to hold a buffer");
bufferSchema.GetClasses().Add(bufferClass);
MgFileFeatureSourceParams sdfParams = new MgFileFeatureSourceParams("OSGeo.SDF", "defaultSrs", wkt, bufferSchema);
featureService.CreateFeatureSource(bufferFeatureResId, sdfParams);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgClassDefinition bufferClass = new MgClassDefinition();
bufferClass.SetName("BufferClass");
MgPropertyDefinitionCollection properties = bufferClass.GetProperties();

MgDataPropertyDefinition idProperty = new MgDataPropertyDefinition("ID");
idProperty.SetDataType(MgPropertyType.Int32);
idProperty.SetReadOnly(true);
idProperty.SetNullable(false);
idProperty.SetAutoGeneration(true);
properties.Add(idProperty);

MgGeometricPropertyDefinition polygonProperty = new MgGeometricPropertyDefinition("BufferGeometry");
polygonProperty.SetGeometryTypes(MgFeatureGeometricType.Surface);
polygonProperty.SetHasElevation(false);
polygonProperty.SetHasMeasure(false);
polygonProperty.SetReadOnly(false);
polygonProperty.SetSpatialContextAssociation("defaultSrs");
properties.Add(polygonProperty);

MgPropertyDefinitionCollection idProperties = bufferClass.GetIdentityProperties();
idProperties.Add(idProperty);

bufferClass.SetDefaultGeometryPropertyName("BufferGeometry");
MgFeatureSchema bufferSchema = new MgFeatureSchema("BufferLayerSchema", "temporary schema to hold a buffer");
bufferSchema.GetClasses().Add(bufferClass);
MgFileFeatureSourceParams sdfParams = new MgFileFeatureSourceParams("OSGeo.SDF", "defaultSrs", wkt, bufferSchema);
featureService.CreateFeatureSource(bufferFeatureResId, sdfParams);

To display features from a temporary feature source in a map, create a layer definition that refers to the feature source. Use the techniques described in Modifying Maps and Layers.

Inserting, Deleting and Updating Features

To change data in a feature source, create an MgFeatureCommandCollection object. This can contain commands to insert, delete, or update features in an FDO data source. The commands are executed sequentially. For FDO providers that support transaction processing, the commands can be treated as a single transaction.

Feature commands can be one of the following:

  • MgDeleteFeatures
  • MgInsertFeatures
  • MgUpdateFeatures

To execute the commands, call MgFeatureService::UpdateFeatures(). The feature class name and property names in any of the feature commands must match the class name and property names in the feature source. If you want to execute feature commands against a MgLayer object, call MgLayer::UpdateFeatures(). The feature class name and feature source id of the MgLayer object is used

For example, to delete all features in a feature class with an identity property ID, execute the following:

PHP

$commands = new MgFeatureCommandCollection();
$deleteCommand = new MgDeleteFeatures($className, "ID like '%'");
$commands->Add($deleteCommand);
$featureService->UpdateFeatures($featureSource, $commands, false);

//You can do this instead if you have a MgLayer object and want to delete features from it
$commands = new MgFeatureCommandCollection();
$deleteCommand = new MgDeleteFeatures($className, "ID like '%'");
$commands->Add($deleteCommand);
$layer->UpdateFeatures($commands);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgDeleteFeature deleteCommand = new MgDeleteFeatures(className, "ID like '%'");
commands.Add(deleteCommand);
featureService.UpdateFeatures(featureSource, commands, false);

//You can do this instead if you have a MgLayer object and want to delete features from it
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgDeleteFeature deleteCommand = new MgDeleteFeatures(className, "ID like '%'");
commands.Add(deleteCommand);
layer.UpdateFeatures(commands);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgDeleteFeature deleteCommand = new MgDeleteFeatures(className, "ID like '%'");
commands.Add(deleteCommand);
featureService.UpdateFeatures(featureSource, commands, false);

//You can do this instead if you have a MgLayer object and want to delete features from it
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgDeleteFeature deleteCommand = new MgDeleteFeatures(className, "ID like '%'");
commands.Add(deleteCommand);
layer.UpdateFeatures(commands);

To insert features, create an MgPropertyCollection object that contains the properties of the new feature. Create an MgInsertFeatures object and add this to the MgFeatureCommandCollection object.

For example, to add a new feature with a single geometry property, execute the following:

PHP

$commands = new MgFeatureCommandCollection();
$properties = new MgPropertyCollection();
$agfByteStream = $agfReaderWriter->Write($geometry);
$geometryProperty = new MgGeometryProperty($propertyName, $agfByteStream);
$properties->Add($geometryProperty);

$insertCommand = new MgInsertFeatures($className, $properties);
$commands->Add($insertCommand);

$featureService->UpdateFeatures($featureSource, $commands, false);

//You can do this instead if you have a MgLayer object and want to update features in it
$commands = new MgFeatureCommandCollection();
$properties = new MgPropertyCollection();
$agfByteStream = $agfReaderWriter->Write($geometry);
$geometryProperty = new MgGeometryProperty($propertyName, $agfByteStream);
$properties->Add($geometryProperty);

$insertCommand = new MgInsertFeatures($className, $properties);
$commands->Add($insertCommand);

$layer->UpdateFeatures($commands);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgPropertyCollection properties = new MgPropertyCollection();
MgByteReader agfByteStream = agfReaderWriter.Write(geometry);
MgGeometryProperty geometryProperty = new MgGeometryProperty(propertyName, agfByteStream);
properties.Add(geometryProperty);

MgInsertFeatures insertCommand = new MgInsertFeatures(className, properties);
commands.Add(insertCommand);

featureService.UpdateFeatures(featureSource, commands, false);

//You can do this instead if you have a MgLayer object and want to update features in it
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgPropertyCollection properties = new MgPropertyCollection();
MgByteReader agfByteStream = agfReaderWriter.Write(geometry);
MgGeometryProperty geometryProperty = new MgGeometryProperty(propertyName, agfByteStream);
properties.Add(geometryProperty);

MgInsertFeatures insertCommand = new MgInsertFeatures(className, properties);
commands.Add(insertCommand);
layer.UpdateFeatures(commands);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgPropertyCollection properties = new MgPropertyCollection();
MgByteReader agfByteStream = agfReaderWriter.Write(geometry);
MgGeometryProperty geometryProperty = new MgGeometryProperty(propertyName, agfByteStream);
properties.Add(geometryProperty);

MgInsertFeatures insertCommand = new MgInsertFeatures(className, properties);
commands.Add(insertCommand);

featureService.UpdateFeatures(featureSource, commands, false);

//You can do this instead if you have a MgLayer object and want to update features in it
MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
MgPropertyCollection properties = new MgPropertyCollection();
MgByteReader agfByteStream = agfReaderWriter.Write(geometry);
MgGeometryProperty geometryProperty = new MgGeometryProperty(propertyName, agfByteStream);
properties.Add(geometryProperty);

MgInsertFeatures insertCommand = new MgInsertFeatures(className, properties);
commands.Add(insertCommand);
layer.UpdateFeatures(commands);

To update existing features, create an MgPropertyCollection object that contains the new values for the properties and a filter expression that selects the correct feature or features. See Querying Feature Data for details about filter expressions.

Creating a Buffer

To create a buffer around a feature, use the MgGeometry::Buffer() method. This returns an MgGeometry object that you can use for further analysis. For example, you could display the buffer by creating a feature in a temporary feature source and adding a new layer to the map. You could also use the buffer geometry as part of a spatial filter. For example, you might want to find all the features within the buffer zone that match certain criteria, or you might want to find all roads that cross the buffer zone.

To create a buffer, get the geometry of the feature to be buffered. If the feature is being processed in an MgFeatureReader as part of a selection, this requires getting the geometry data from the feature reader and converting it to an MgGeometry object. For example:

PHP

$geometryData = $featureReader->GetGeometry($geometryName);
$featureGeometry = $agfReaderWriter->Read($geometryData);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgByteReader geometryData = featureReader.GetGeometry(geometryName);
MgGeometry featureGeometry = agfReaderWriter.Read(geometryData);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgByteReader geometryData = featureReader.GetGeometry(geometryName);
MgGeometry featureGeometry = agfReaderWriter.Read(geometryData);

If the buffer is to be calculated using coordinate system units, create an MgCoordinateSystemMeasure object from the coordinate system for the map. For example:

PHP

$mapWktSrs = $currentMap->GetMapSRS();
$coordSysFactory = new MgCoordinateSystemFactory();
$srs = $coordSysFactory->Create($mapWktSrs);
$srsMeasure = $srs->GetMeasure();

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
String mapWktSrs = currentMap.GetMapSRS();
MgCoordinateSystemFactory coordSysFactory = new MgCoordinateSystemFactory();
MgCoordianteSystem srs = coordSysFactory.Create(mapWktSrs);
MgCoordinateSystemMeasure srsMeasure = srs.GetMeasure();

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
String mapWktSrs = currentMap.GetMapSRS();
MgCoordinateSystemFactory coordSysFactory = new MgCoordinateSystemFactory();
MgCoordianteSystem srs = coordSysFactory.Create(mapWktSrs);
MgCoordinateSystemMeasure srsMeasure = srs.GetMeasure();

Use the coordinate system measure to determine the buffer size in the coordinate system, and create the buffer object from the geometry to be buffered.

PHP

$srsDist = $srs->ConvertMetersToCoordinateSystemUnits($bufferDist);
$bufferGeometry = $featureGeometry->Buffer($srsDist, $srsMeasure);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
double srsDist = srs.ConvertMetersToCoordinateSystemUnits(bufferDist);
MgGeometry bufferGeometry = featureGeometry.Buffer(srsDist, srsMeasure);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
double srsDist = srs.ConvertMetersToCoordinateSystemUnits(bufferDist);
MgGeometry bufferGeometry = featureGeometry.Buffer(srsDist, srsMeasure);

To display the buffer in the map, perform the following steps:

  • Create a feature source for the buffer.
  • Insert a buffer feature in the feature source.
  • Create a layer that references the feature source.
  • Add the layer to the map and make it visible.

To use the buffer as part of a query, create a spatial filter using the buffer geometry, and use this in a call to MgFeatureService::SelectFeatures() or MgLayer::SelectFeatures(). For example, the following code selects parcels inside the buffer area that are of type “MFG”. You can use the MgFeatureReader to perform tasks like generating a report of the parcels, or creating a new layer that puts point markers on each parcel.

PHP

$queryOptions = new MgFeatureQueryOptions();
$queryOptions->SetFilter("RTYPE = 'MFG'");
$queryOptions->SetSpatialFilter('SHPGEOM', $bufferGeometry, MgFeatureSpatialOperations::Inside);
/*
// Old way, pre MapGuide OS 2.0. Kept here for reference
$featureResId = new MgResourceIdentifier("Library://Samples/Sheboygan/Data/Parcels.FeatureSource");
$featureReader = $featureService->SelectFeatures($featureResId, "Parcels", $queryOptions);
*/

// New way, post MapGuide OS 2.0
$featureReader = $layer->SelectFeatures($queryOptions);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgFeatureQueryOptions queryOptions = new MgFeatureQueryOptions();
queryOptions.SetFilter("RTYPE = 'MFG'");
queryOptions.SetSpatialFilter("SHPGEOM", bufferGeometry, MgFeatureSpatialOperations.Inside);
MgResourceIdentifier featureResId = new MgResourceIdentifier("Library://Samples/Sheboygan/Data/Parcels.FeatureSource");
MgFeatureReader featureReader = featureService.SelectFeatures(featureResId, "Parcels", queryOptions);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace
MgFeatureQueryOptions queryOptions = new MgFeatureQueryOptions();
queryOptions.SetFilter("RTYPE = 'MFG'");
queryOptions.SetSpatialFilter("SHPGEOM", bufferGeometry, MgFeatureSpatialOperations.Inside);
MgResourceIdentifier featureResId = new MgResourceIdentifier("Library://Samples/Sheboygan/Data/Parcels.FeatureSource");
MgFeatureReader featureReader = featureService.SelectFeatures(featureResId, "Parcels", queryOptions);

Example

This example builds on the example from Working with the Active Selection. Instead of listing the parcels in the selection, it creates a series of concentric buffers around the selection, showing increasing distance. The code sections below contain the significant additions in this example. The complete source code is available with the Developer’s Guide samples.

Because this example modifies the map, it must refresh the map when it loads, by executing a JavaScript function. Add the function to the page.

<script language="javascript">
function OnPageLoad()
{
    parent.parent.Refresh();
}
</script>

Add an OnLoad command to the <body> element:

<body onLoad="OnPageLoad()">

The example uses a temporary map layer named Buffer to store the buffer feature. It creates a feature source and the layer if it does not exist. Otherwise, it deletes any existing features before creating the new buffer. The functions CreateBufferFeatureSource() and CreateBufferLayer() are in bufferfunctions.php, which is described below.

PHP

include 'bufferfunctions.php';
$bufferRingSize = 100; // measured in metres
$bufferRingCount = 5;

// Set up some objects for coordinate conversion

$mapWktSrs = $map->GetMapSRS();
$agfReaderWriter = new MgAgfReaderWriter();
$wktReaderWriter = new MgWktReaderWriter();
$coordinateSystemFactory = new MgCoordinateSystemFactory();
$srs = $coordinateSystemFactory->Create($mapWktSrs);
$srsMeasure = $srs->GetMeasure();

// Check for a buffer layer. If it exists, delete
// the current features.
// If it does not exist, create a feature source and
// a layer to hold the buffer.

// This is how things can be done now
$layerIndex = $map->GetLayers()->IndexOf('Buffer');
if ($layerIndex < 0)
{
    // The layer does not exist and must be created.

    $bufferFeatureResId = new MgResourceIdentifier("Session:" . $sessionId . "//Buffer.FeatureSource");
    CreateBufferFeatureSource($featureService, $mapWktSrs, $bufferFeatureResId);
    $bufferLayer = CreateBufferLayer($resourceService, $bufferFeatureResId, $sessionId);
    $map->GetLayers()->Insert(0, $bufferLayer);
}
else
{
    $bufferLayer = $map->GetLayers()->GetItem($layerIndex);
    $commands = new MgFeatureCommandCollection();
    $commands->Add(new MgDeleteFeatures('BufferClass', "ID like '%'"));

    $bufferLayer->UpdateFeatures($commands);
}

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
int bufferRingSize = 100; // measured in metres
int bufferRingCount = 5;

// Set up some objects for coordinate conversion

String mapWktSrs = map.GetMapSRS();
MgAgfReaderWriter agfReaderWriter = new MgAgfReaderWriter();
MgWktReaderWriter wktReaderWriter = new MgWktReaderWriter();
MgCoordinateSystemFactory coordinateSystemFactory = new MgCoordinateSystemFactory();
MgCoordinateSystem srs = coordinateSystemFactory.Create(mapWktSrs);
MgMeasure srsMeasure = srs.GetMeasure();

BufferHelper helper = new BufferHelper(Server);

// Check for a buffer layer. If it exists, delete
// the current features.
// If it does not exist, create a feature source and
// a layer to hold the buffer.

MgLayer bufferLayer = null;
int layerIndex = map.GetLayers().IndexOf("Buffer");
if (layerIndex < 0)
{
    // The layer does not exist and must be created.

    MgResourceIdentifier bufferFeatureResId = new MgResourceIdentifier("Session:" + sessionId + "//Buffer.FeatureSource");
    helper.CreateBufferFeatureSource(featureService, mapWktSrs, bufferFeatureResId);
    bufferLayer = helper.CreateBufferLayer(resourceService, bufferFeatureResId, sessionId);
    map.GetLayers().Insert(0, bufferLayer);
}
else
{
    bufferLayer = (MgLayer)map.GetLayers().GetItem(layerIndex);
    MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
    commands.Add(new MgDeleteFeatures("BufferClass", "ID like '%'"));

    bufferLayer.UpdateFeatures(commands);
}

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace

The geometries for the selected features are merged into a single multi-geometry. Then a series of concentric buffers is created and added to the feature source. The style for the layer, which is set when function CreateBufferLayer() processes bufferlayerdefinition.xml, should define the buffer features to be partly transparent. When they are drawn on the map, the rings get progressively darker towards the center of the buffer area.

PHP

// Process each item in the MgFeatureReader.
// Merge them into a single feature.

$inputGeometries = new MgGeometryCollection();
while ($featureReader->ReadNext())
{
    $featureGeometryData = $featureReader->GetGeometry('SHPGEOM');
    $featureGeometry = $agfReaderWriter->Read($featureGeometryData);

    $inputGeometries->Add($featureGeometry);
}

$geometryFactory = new MgGeometryFactory();
$mergedFeatures = $geometryFactory->CreateMultiGeometry($inputGeometries);

// Add buffer features to the temporary feature source.
// Create multiple concentric buffers to show area.

$commands = new MgFeatureCommandCollection();
for ($bufferRing = 0; $bufferRing < $bufferRingCount; $bufferRing++)
{
    $bufferDist = $srs->ConvertMetersToCoordinateSystemUnits($bufferRingSize * ($bufferRing + 1));
    $bufferGeometry = $mergedFeatures->Buffer($bufferDist, $srsMeasure);

    $properties = new MgPropertyCollection();
    $properties->Add(new MgGeometryProperty('BufferGeometry', $agfReaderWriter->Write($bufferGeometry)));

    $commands->Add(new MgInsertFeatures('BufferClass', $properties));
}

$results = $bufferLayer->UpdateFeatures($commands);

$bufferLayer->SetVisible(true);
$bufferLayer->ForceRefresh();
$bufferLayer->SetDisplayInLegend(true);

//If you created a MgMap with a MgSiteConnection
$map->Save();

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace
MgGeometryCollection inputGeometries = new MgGeometryCollection();
while (featureReader.ReadNext())
{
    MgByteReader featureGeometryData = featureReader.GetGeometry("SHPGEOM");
    MgGeometry featureGeometry = agfReaderWriter.Read(featureGeometryData);

    inputGeometries.Add(featureGeometry);
}

MgGeometryFactory geometryFactory = new MgGeometryFactory();
MgGeometry mergedGeometries = geometryFactory.CreateMultiGeometry(inputGeometries);

// Add buffer features to the temporary feature source.
// Create multiple concentric buffers to show area.
// If the stylization for the layer draws the features
// partially transparent, the concentric rings will be
// progressively darker towards the center.
// The stylization is set in the layer template file, which
// is used in function CreateBufferLayer().

MgFeatureCommandCollection commands = new MgFeatureCommandCollection();
for (int bufferRing = 0; bufferRing < bufferRingCount; bufferRing++)
{
    double bufferDist = srs.ConvertMetersToCoordinateSystemUnits(bufferRingSize * (bufferRing + 1));
    MgGeometry bufferGeometry = mergedGeometries.Buffer(bufferDist, srsMeasure);

    MgPropertyCollection properties = new MgPropertyCollection();
    properties.Add(new MgGeometryProperty("BufferGeometry", agfReaderWriter.Write(bufferGeometry)));

    commands.Add(new MgInsertFeatures("BufferClass", properties));
}

bufferLayer.UpdateFeatures(commands);

bufferLayer.SetVisible(true);
bufferLayer.ForceRefresh();
bufferLayer.SetDisplayInLegend(true);
map.Save();

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace

//No code sample available yet.

The functions CreateBufferFeatureSource() and CreateBufferLayer() are in bufferfunctions.php. CreateBufferFeatureSource() creates a temporary feature source, with a single feature class, BufferClass. The feature class has two properties, ID and BufferGeometry. ID is autogenerated, so it does not need to be added with a new feature. CreateBufferLayer() modifies a layer definition from an external file and saves it to the repository. For more details, see Modifying Maps and Layers.

PHP

function CreateBufferFeatureSource($featureService, $wkt, $bufferFeatureResId)
{
    $bufferClass = new MgClassDefinition();
    $bufferClass->SetName('BufferClass');
    $properties = $bufferClass->GetProperties();
    $idProperty = new MgDataPropertyDefinition('ID');
    $idProperty->SetDataType(MgPropertyType::Int32);
    $idProperty->SetReadOnly(true);
    $idProperty->SetNullable(false);
    $idProperty->SetAutoGeneration(true);
    $properties->Add($idProperty);
    $polygonProperty = new MgGeometricPropertyDefinition('BufferGeometry');
    $polygonProperty->SetGeometryTypes(MgFeatureGeometricType::Surface);
    $polygonProperty->SetHasElevation(false);
    $polygonProperty->SetHasMeasure(false);
    $polygonProperty->SetReadOnly(false);
    $polygonProperty->SetSpatialContextAssociation('defaultSrs');
    $properties->Add($polygonProperty);
    $idProperties = $bufferClass->GetIdentityProperties();
    $idProperties->Add($idProperty);
    $bufferClass->SetDefaultGeometryPropertyName('BufferGeometry');
    $bufferSchema = new MgFeatureSchema('BufferLayerSchema', 'temporary schema to hold a buffer');
    $bufferSchema->GetClasses()->Add($bufferClass);
    $sdfParams = new MgFileFeatureSourceParams('OSGeo.SDF', 'defaultSrs', $wkt, $bufferSchema);
    $featureService->CreateFeatureSource($bufferFeatureResId, $sdfParams);
}

function CreateBufferLayer($resourceService, $bufferFeatureResId, $sessionId)
{
    // Load the layer definition template into
    // a PHP DOM object, find the "ResourceId" element, and
    // modify its content to reference the temporary
    // feature source.
    $doc = DOMDocument::load('bufferlayerdefinition.xml');
    $featureSourceNode = $doc->getElementsByTagName('ResourceId')->item(0);
    $featureSourceNode->nodeValue = $bufferFeatureResId->ToString();
    // Get the updated layer definition from the DOM object
    // and save it to the session repository using the
    // ResourceService object.
    $layerDefinition = $doc->saveXML();
    $byteSource = new MgByteSource($layerDefinition, strlen($layerDefinition));
    $byteSource->SetMimeType(MgMimeType::Xml);
    $tempLayerResId = new MgResourceIdentifier("Session:" . $sessionId . "//Buffer.LayerDefinition");
    $resourceService->SetResource($tempLayerResId, $byteSource->GetReader(), null);
    // Create an MgLayer object based on the new layer definition
    // and return it to the caller.
    $bufferLayer = new MgLayer($tempLayerResId, $resourceService);
    $bufferLayer->SetName("Buffer");
    $bufferLayer->SetLegendLabel("Buffer");
    $bufferLayer->SetDisplayInLegend(true);
    $bufferLayer->SetSelectable(false);
    return $bufferLayer;
}

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace

//NOTE: This is only subset of the BufferHelper relevant to this particular chapter
//of the developer's guide. For the full source, see bufferfunctions.aspx from the
//.net Developer Guide sample.

public class BufferHelper
{
    private HttpServerUtility _server;

    public BufferHelper(HttpServerUtility server)
    {
        _server = server;
    }

    HttpServerUtility Server { get { return _server; } }

    public void CreateBufferFeatureSource(MgFeatureService featureService, String wkt, MgResourceIdentifier bufferFeatureResId)
    {
        MgClassDefinition bufferClass = new MgClassDefinition();
        bufferClass.SetName("BufferClass");
        MgPropertyDefinitionCollection properties = bufferClass.GetProperties();

        MgDataPropertyDefinition idProperty = new MgDataPropertyDefinition("ID");
        idProperty.SetDataType(MgPropertyType.Int32);
        idProperty.SetReadOnly(true);
        idProperty.SetNullable(false);
        idProperty.SetAutoGeneration(true);
        properties.Add(idProperty);

        MgGeometricPropertyDefinition polygonProperty = new MgGeometricPropertyDefinition("BufferGeometry");
        polygonProperty.SetGeometryTypes(MgFeatureGeometricType.Surface);
        polygonProperty.SetHasElevation(false);
        polygonProperty.SetHasMeasure(false);
        polygonProperty.SetReadOnly(false);
        polygonProperty.SetSpatialContextAssociation("defaultSrs");
        properties.Add(polygonProperty);

        MgPropertyDefinitionCollection idProperties = bufferClass.GetIdentityProperties();
        idProperties.Add(idProperty);

        bufferClass.SetDefaultGeometryPropertyName("BufferGeometry");

        MgFeatureSchema bufferSchema = new MgFeatureSchema("BufferLayerSchema", "temporary schema to hold a buffer");
        bufferSchema.GetClasses().Add(bufferClass);

        MgFileFeatureSourceParams sdfParams = new MgFileFeatureSourceParams("OSGeo.SDF", "defaultSrs", wkt, bufferSchema);

        featureService.CreateFeatureSource(bufferFeatureResId, sdfParams);
    }

    public MgLayer CreateBufferLayer(MgResourceService resourceService, MgResourceIdentifier bufferFeatureResId, String sessionId)
    {
        // Load the layer definition template into
        // a XmlDocument object, find the "ResourceId" element, and
        // modify its content to reference the temporary
        // feature source.

        XmlDocument doc = new XmlDocument();
        doc.Load(Server.MapPath("bufferlayerdefinition.xml"));
        XmlNode featureSourceNode = doc.GetElementsByTagName("ResourceId")[0];
        featureSourceNode.InnerText = bufferFeatureResId.ToString();

        // Get the updated layer definition from the XmlDocument
        // and save it to the session repository using the
        // ResourceService object.

        MgByteSource byteSource = null;
        using (MemoryStream ms = new MemoryStream())
        {
            doc.Save(ms);
            ms.Position = 0L;

            //Note we do this to ensure our XML content is free of any BOM characters
            byte [] layerDefinition = ms.ToArray();
            Encoding utf8 = Encoding.UTF8;
            String layerDefStr = new String(utf8.GetChars(layerDefinition));
            layerDefinition = new byte[layerDefStr.Length-1];
            int byteCount = utf8.GetBytes(layerDefStr, 1, layerDefStr.Length-1, layerDefinition, 0);

            byteSource = new MgByteSource(layerDefinition, layerDefinition.Length);
            byteSource.SetMimeType(MgMimeType.Xml);
        }

        MgResourceIdentifier tempLayerResId = new MgResourceIdentifier("Session:" + sessionId + "//Buffer.LayerDefinition");

        resourceService.SetResource(tempLayerResId, byteSource.GetReader(), null);

        // Create an MgLayer object based on the new layer definition
        // and return it to the caller.

        MgLayer bufferLayer = new MgLayer(tempLayerResId, resourceService);
        bufferLayer.SetName("Buffer");
        bufferLayer.SetLegendLabel("Buffer");
        bufferLayer.SetDisplayInLegend(true);
        bufferLayer.SetSelectable(false);

        return bufferLayer;
    }
}

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace

//No code sample available yet

There is an additional example in the Developer’s Guide samples. It queries the parcels in the buffer area and selects parcels that match certain criteria. The selection is done using a query that combines a basic filter and a spatial filter.

PHP

$bufferDist = $srs->ConvertMetersToCoordinateSystemUnits($bufferRingSize);
$bufferGeometry = $mergedGeometries->Buffer($bufferDist, $srsMeasure);
// Create a filter to select parcels within the buffer. Combine
// a basic filter and a spatial filter to select all parcels
// within the buffer that are of type "MFG".
$queryOptions = new MgFeatureQueryOptions();
$queryOptions->SetFilter("RTYPE = 'MFG'");
$queryOptions->SetSpatialFilter('SHPGEOM', $bufferGeometry, MgFeatureSpatialOperations::Inside);

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace

double bufferDist = srs.ConvertMetersToCoordinateSystemUnits(bufferRingSize);
MgGeometry bufferGeometry = mergedGeometries.Buffer(bufferDist, srsMeasure);

// Create a filter to select parcels within the buffer. Combine
// a basic filter and a spatial filter to select all parcels
// within the buffer that are of type "MFG".

queryOptions = new MgFeatureQueryOptions();
queryOptions.SetFilter("RTYPE = 'MFG'");
queryOptions.SetSpatialFilter("SHPGEOM", bufferGeometry, MgFeatureSpatialOperations.Inside);

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace

double bufferDist = srs.ConvertMetersToCoordinateSystemUnits(bufferRingSize);
MgGeometry bufferGeometry = mergedGeometries.Buffer(bufferDist, srsMeasure);

// Create a filter to select parcels within the buffer. Combine
// a basic filter and a spatial filter to select all parcels
// within the buffer that are of type "MFG".

queryOptions = new MgFeatureQueryOptions();
queryOptions.SetFilter("RTYPE = 'MFG'");
queryOptions.SetSpatialFilter("SHPGEOM", bufferGeometry, MgFeatureSpatialOperations.Inside);

It creates an additional feature source that contains point markers for each of the selected parcels.

PHP

// Get the features from the feature source,
// determine the centroid of each selected feature, and
// add a point to the ParcelMarker layer to mark the
// centroid.
// Collect all the points into an MgFeatureCommandCollection,
// so they can all be added in one operation.

$parcelMarkerCommands = new MgFeatureCommandCollection();
while ($featureReader->ReadNext())
{
    $byteReader = $featureReader->GetGeometry('SHPGEOM');
    $geometry = $agfReaderWriter->Read($byteReader);
    $point = $geometry->GetCentroid();

    // Create an insert command for this parcel.
    $properties = new MgPropertyCollection();

    $properties->Add(new MgGeometryProperty('ParcelLocation', $agfReaderWriter->Write($point)));
    $parcelMarkerCommands->Add(new MgInsertFeatures('ParcelMarkerClass', $properties));
}
$featureReader->Close();

if ($parcelMarkerCommands->GetCount() > 0)
{
    $parcelMarkerLayer->UpdateFeatures($parcelMarkerCommands);
}
else
{
    echo '</p><p>No parcels within the buffer area match.';
}

.net (C#)

//This code fragment assumes you have imported the OSGeo.MapGuide namespace

// Get the features from the feature source,
// determine the centroid of each selected feature, and
// add a point to the ParcelMarker layer to mark the
// centroid.
// Collect all the points into an MgFeatureCommandCollection,
// so they can all be added in one operation.

MgFeatureCommandCollection parcelMarkerCommands = new MgFeatureCommandCollection();
while (featureReader.ReadNext())
{
    MgByteReader byteReader = featureReader.GetGeometry("SHPGEOM");
    MgGeometry geometry = agfReaderWriter.Read(byteReader);
    MgPoint point = geometry.GetCentroid();

    // Create an insert command for this parcel.
    MgPropertyCollection properties = new MgPropertyCollection();
    properties.Add(new MgGeometryProperty("ParcelLocation", agfReaderWriter.Write(point)));
    parcelMarkerCommands.Add(new MgInsertFeatures("ParcelMarkerClass", properties));
}
featureReader.Close();

if (parcelMarkerCommands.GetCount() > 0)
{
    parcelMarkerLayer.UpdateFeatures(parcelMarkerCommands);
}
else
{
    Response.Write("</p><p>No parcels within the buffer area match.");
}

Java

//This code fragment assumes you have imported the org.osgeo.mapguide namespace

// Get the features from the feature source,
// determine the centroid of each selected feature, and
// add a point to the ParcelMarker layer to mark the
// centroid.
// Collect all the points into an MgFeatureCommandCollection,
// so they can all be added in one operation.

MgFeatureCommandCollection parcelMarkerCommands = new MgFeatureCommandCollection();
while (featureReader.ReadNext())
{
    MgByteReader byteReader = featureReader.GetGeometry("SHPGEOM");
    MgGeometry geometry = agfReaderWriter.Read(byteReader);
    MgPoint point = geometry.GetCentroid();

    // Create an insert command for this parcel.
    MgPropertyCollection properties = new MgPropertyCollection();
    properties.Add(new MgGeometryProperty("ParcelLocation", agfReaderWriter.Write(point)));
    parcelMarkerCommands.Add(new MgInsertFeatures("ParcelMarkerClass", properties));
}
featureReader.Close();

if (parcelMarkerCommands.GetCount() > 0)
{
    parcelMarkerLayer.UpdateFeatures(parcelMarkerCommands);
}
else
{
    response.getWriter().Write("</p><p>No parcels within the buffer area match.");
}