Monday, 22 July 2013

Scripting and Automating Maestro: An example scenario

One of the major new features of the recently released MapGuide Maestro 5.0 (windows-only feature atm. Sorry) is the embedded IronPython scripting engine. Adding such support to Maestro allows users to automate repetitive tasks in the Maestro application with snippets of Python code and/or loading existing libraries of Python functions.

For this post, I'll be talking about how this particular feature saved me several hours of work.

Recently on a client job, I had a Map Definition containing upwards of 50+ layers whose scale ranges I needed to curtail down to a specific minimum/maximum ranges to improve rendering performance. The classic naive approach to tacking this problem would've been to open each individual layer, edit its minimum/maximum scale range values, save the layer back and repeat this process for each layer.

Despite all the authoring productivity improvements I've made to Maestro, this is still the only way to carry out this task in the most "efficient" manner before the introduction of the IronPython scripting engine short of writing a disposable .net program to do this using the Maestro API.

But since we had this scripting engine in place, I'd figured I would eat my own dogfood and see how this new feature could eliminate me having to manually modify the scale ranges of those 50+ layers "by hand".

Several minutes of noodling around in the IronPython console gave me this re-usable Python function

def FixScaleRangesMinMax(conn, folder, min, max):
    Modifies the first scale range of all Layer Definitions under the specified folder to the specified
    minimum and Maximum scales
    resList = conn.ResourceService.GetRepositoryResources(folder, "LayerDefinition")
    for child in resList.Children:
        ldf = conn.ResourceService.GetResource(child.ResourceId)
        print "Fixing: %s" % (child.ResourceId)
        # vsr is a IVectorScaleRange instance
        vsr = ldf.SubLayer.GetScaleRangeAt(0)
        vsr.MinScale = min
        # MinScale is a nullable property, so you also need to set the respective MinScaleSpecified property to true
        # in order for this change to be persisted
        vsr.MinScaleSpecified = True
        vsr.MaxScale = max
        # MaxScale is a nullable property, so you also need to set the respective MaxScaleSpecified property to true
        # in order for this change to be persisted
        vsr.MaxScaleSpecified = True
        # Save the changes back
        print "Scale range set to [%d, %d]" % (min, max)
        print "Saved: %s" % (child.ResourceId)

As a result, instead of having to manually open 50+ layers to edit their scale ranges to be between 100 and 10000, I can just type this into the IronPython console:

>>> conn = app.GetConnection(app.GetConnectionNames()[0])
>>> FixScaleRangesMinMax(conn, "Library://Folder/Containing/My/Layers/", 100, 10000)

And have the IronPython scripting engine do the equivalent work in just a few seconds!

It turns out my above scenario is pretty much what this particular ticket describes, but I closed down that ticket for this reason. The desired enhancement in that ticket is a real extreme corner-case situation that can be easily accommodated by this very feature with some some Python scripts to do the dirty work.

If such a function proves useful to you in the future, you can write such code into actual python script files (.py) and load this code into the IronPython console via the Run File command

But to save you some time, here's one I prepared earlier. The script contains the above function and 3 other variants to deal with cases where you want the min scale to be 0 and/or the max scale to be infinity. You can load this file in as per the above process and those functions will then be available for you to call from the IronPython console.

I hope this sheds some insight into how this new feature can save you plenty of time and maybe give you some ideas too. I also encourage you to share any scripts you may/will write with this feature.

1 comment:

David said...


this works great. As the Python console is interractive it could be usefull that you enable ctrl+c from this console :)

Thank you