Wednesday, 20 June 2012

MapGuide's undocumented custom aggregate functions

Deep inside MapGuide's server codebase, there is a series of expression functions which you can use for certain types of aggregate queries. I say undocumented because if you try to google for these functions and what they do, I'm sure you'll either find nothing or it will be this post, a few days after publishing :)

If you ever looked at the code for the Fusion theme widget or the theme examples for the AJAX viewer, you'll notice that there is a series of mysterious expression functions being called:
  • UNIQUE
  • EQUAL_DIST
  • STDEV_DIST
  • QUANT_DIST
  • JENK_DIST
But in actuality, there's references to even more functions in the MapGuide code:
  • MINIMUM
  • MAXIMUM
  • MEAN
  • STD_DEV
  • EXTENT
These functions do actually serve a useful purpose. Because not all FDO providers are created equal, it means that some providers are somewhat lacking in capabilities, which is critical if we want to say: Fetch a distinct set of values for theming. These functions serve to provide a somewhat level playing field for all FDO providers. It is through these functions that allow for some FDO providers to be able to do things that their actual capabilities would not be able to do.

One thing that's strange about these functions (besides that they exist :-)) is that they do not follow the normal syntax rules of your normal FDO expressions. This is probably because these functions already existed in MapGuide from the very beginning and pre-dates the introduction of the FDO expression engine and its expression syntax rules. What this means is that you can't do fancy function call chaining (eg. FunctionA(FunctionB()) ) for these particular functions, you must pass in literal values for whatever parameters are applicable.

So without further ado, here's some actual documentation. As a general note, all of these functions are called in the following fashion (PHP example):


//$featSvc is an instance of MgFeatureService

$fsId = new MgResourceIdentifier("Library://Samples/Sheboygan/Data/Parcels.FeatureSource");
$className = "Parcels";
$expr = "UNIQUE(RTYPE)"; //The expression containing the custom aggregate function
$query = new MgFeatureAggregateOptions();
$query->AddComputedProperty("RESULT", $expr);
$rdr = $featSvc->SelectAggregate($fsId, $className, $query);
while ($rdr->ReadNext())
{
    //Output the result
}
$rdr->Close();


As a second general note, do not take this blog post as the definitive fact about these functions. This post is just the result of my own observations of the MapGuide Server source code. I may have gotten a few bits incorrect here and there. But the general description and usages should be correct.

UNIQUE

The UNIQUE function returns a list of unique values for a given FDO property. Maestro and Studio both use this function to fetch a unique list of values for generating themes. It's signature is:
  • UNIQUE( property_name )
  • property_name is the FDO property which this function will return a list of unique values for
This function will return a value in the list which can be accessed through the aliased property you have mapped this function to. ReadNext() on the MgDataReader will advance to the next unique value on the list.

EQUAL_DIST


The EQUAL_DIST function returns an equal distribution of the given FDO property. The property must be a numerical property. It's signature is:
  • EQUAL_DIST( property_name, numRules, minValue, maxValue )
  • property_name is the FDO property which this function will create an equal distribution out of. This must be a string value and cannot be an FDO expression that evaluates to a string
  • numRules indicates the number of rules to create in this distribution. It must be an integer value and cannot be an FDO expression that evaluates to an integer
  • minValue indicates the minimum allowed value in this distribution. It must be a numerical value and cannot be an FDO expression that evaluates to a numeric value
  • maxValue indicates the maximum allowed value in this distribution. It must be a numerical value and cannot be an FDO expression that evaluates to a numeric value
This function will return a value in the distribution which can be accessed through the aliased property you have mapped this function to. ReadNext() on the MgDataReader will advance to the next value in the distribution. 


For reasons not known to me, this function will actually return (numRules+1)results. The theme example in the AJAX viewer samples uses this function (and other distribution functions below) to generate the themed layer definition. The 

STDEV_DIST


The STDEV_DIST function returns a standard deviation distribution of the given FDO property. The property must be a numerical property. It's signature is:
  • STDEV_DIST( property_name, numRules, minValue, maxValue )
Parameter constraints and results processing is the same as EQUAL_DIST

QUANT_DIST


The QUANT_DIST function returns a quantile distribution of the given FDO property. The property must be a numerical property. It's signature is:
  • QUANT_DIST( property_name, numRules, minValue, maxValue )
Parameter constraints and results processing is the same as EQUAL_DIST

JENK_DIST

The JENK_DIST function returns a Jenks distribution of the given FDO property. The property must be a numerical property. It's signature is:
  • JENK_DIST( property_name )
Parameter constraints and results processing is the same as EQUAL_DIST

MINIMUM


The MINIMUM function returns a smallest value of the given FDO property. It's signature is:
  • MINIMUM( property_name )
  • property_name is the FDO property which this function will find the minimum value of. This must be a string value and cannot be an FDO expression that evaluates to a string  
The function will return exactly one result containing the minimum value and can be accessed in the MgDataReader by using the aliased property that this function is mapped to.

MAXIMUM


The MAXIMUM function returns a largest value of the given FDO property. It's signature is:
  • MAXIMUM( property_name )
  • property_name is the FDO property which this function will find the maximum value of. This must be a string value and cannot be an FDO expression that evaluates to a string
The function will return exactly one result containing the maximum value and can be accessed in the MgDataReader by using the aliased property that this function is mapped to.

MEAN


The MEAN function returns a mean value of the given FDO property. It's signature is:
  • MEAN( property_name )
  • property_name is the FDO property which this function will find the mean value of. This must be a string value and cannot be an FDO expression that evaluates to a string
The function will return exactly one result containing the mean value and can be accessed in the MgDataReader by using the aliased property that this function is mapped to.

STD_DEV

The STD_DEV function returns the standard deviation of the given FDO property. It's signature is:
  • STD_DEV( property_name )
  • property_name is the FDO property which this function will find the standard deviation of. This must be a string value and cannot be an FDO expression that evaluates to a string
The function will return exactly one result containing the standard deviation value and can be accessed in the MgDataReader by using the aliased property that this function is mapped to.


EXTENT


The EXTENT function returns the bounding box of the given FDO geometry property. It's signature is:
  • EXTENT( property_name )
  • property_name is the FDO property which this function will compute the bounding box of. This must be a string value and cannot be an FDO expression that evaluates to a string
The function will return exactly one result containing the MgByteReader of the computed bounding box geometry and can be accessed in the MgDataReader by using the aliased property that this function is mapped to.

NOTE: Prior to this revision (ie. MapGuide 2.4 beta 1 and older), bad geometries will trip up this function. So if using this function throws an exception, you know you have at least one bad geometry in that data.


I've added a PHP sample here to that demonstrates the use of these functions.

3 comments:

Riccardo Gaeta said...

Hi, very cool tip, thanks a lot!
But I would like to ask if it's possible to sort the results of the aggregate query. I've tried with a simple:
$stringCollection->Add($propertyName);
$query->SetOrderingFilter($stringCollection, MgOrderingOption::Ascending);

But MG give me error on various cpp files.
How can I solve this?

Thanks very much!
Riccardo

Jackie Ng said...

Sorry, you can't sort the results of these custom aggregate functions.

You'll have to collect the individual result values from the reader and that sort that collection before outputting the values from that collection.

Riccardo Gaeta said...

I've solved by putting the results in an array and sort it.
If could be of some interest to someone at the end I attach the part of code.
Thanks again.
Riccardo

$propertyType = $rdr->GetPropertyType($propertyName);
//Order the results by creating an array
$values = array();
while ($rdr->ReadNext())
{
//GetValueFromReader is a function that extract the value by MgProperty type.
//Something like:
//switch ($propertyType) {
//case MgPropertyType::String :
//$val = $rdr->GetString($propertyName);
//break; }
//return $val;
//etc...
$value = GetValueFromReader($rdr, $propertyType, $propertyName);
array_push($values, $value);
}
if ($propertyType == MgPropertyType::String)
sort($values, SORT_STRING);
else
sort($values, SORT_NUMERIC);
$rdr->Close();