Friday, 26 July 2013

About that AS YET UN-ANNOUNCED PROJECT

Ok, I might as well unveil this thing in case people start wondering what the hell I was talking around 9 months ago.

The AS YET UN-ANNOUNCED PROJECT is mapguide4j

What was (is) mapguide4j?

mapguide4j was an experiment of mine to dogfood the MapGuide Java wrapper API and test the feasibility of the following things:

1. The feasibility of the Java wrapper to the MapGuide API

The MapGuide Java wrapper is a bit of an unknown quantity. All we knew is that Java ranked the least popular of the 3 development environments in that poll some time ago. Nobody I know has actually showcased a MapGuide application built with the Java wrapper API. So why would this be the case?

  • Was Java just not popular enough?
  • Was the Java wrapper un-reliable?
  • Was the environment setup just too cumbersome?

Despite my reservations about the Java language (which are still true when building this thing), the Java platform is still a major player in the server/enterprise space. The fact that some of the most popular geospatial software written in Java is proof the language and platform are not dying anytime soon.

Could it be the reliability? Possibly. We knew that before the 2.4 release of MapGuide Open Source, this wrapper was leaking memory like a bursting dam. If every "new MgClassName()" statement causes an irrecoverable memory-leak, then it would be no surprise that application reliability/stability would go down the drain

Could it be the cumbersome environment setup? Most likely. The current documentation doesn't really tell you how to actually develop a MapGuide application in Java. Hey, I'd improve this part of the documentation if I myself actually knew but I don't so I am just as clueless as you lot :). Other development environments are easy and well-defined:

  • .net: Start Visual Studio. Make a new ASP.net application, reference your MapGuide assemblies (or pull them down with nuget) and away you go.
  • PHP: Make a new folder under your www directory, start creating/editing your PHP files with your favourite text editor and away you go.
  • Java: ???

What if you're using Eclipse? Netbeans? IntelliJ IDEA? What do you do? I have no idea!

So yeah, building a MapGuide application in Java is fraught with many question marks about development workflow and reliability, something that we hoped to answer (at least partially) with the mapguide4j project.

2. The feasibility of building a MapGuide Web Tier in a high-level language free of Apache / IIS / Tomcat

Though mapguide4j's focus is on the MapGuide Java wrapper API, the question equally applies to .net and PHP. The default Web Tier ties into Apache or IIS and thus any MapGuide applications you build has to tie into Apache or IIS as well. This default setup can hamper some types of MapGuide applications you want to build, which mapguide4j turned out to be one of these special cases.

3. A truly portable Web Tier in a high-level language

If our fully managed Web Tier was done in .net then you're realistically only gonna be able to run it only on Windows which is not what we want. We wanted the ability to have this Web Tier work on both Windows and Linux in a high level language, so it was either PHP or Java. PHP had the Apache dependency, whereas Java does not (depending on the choice of framework we want to use).

4. Getting re-acquainted with Java

My Java skills were rusty. So mapguide4j was also a convenient exercise in getting re-acquainted with this language (and everything about the language that absolutely annoys me :))

mapguide4j overview

Ok, so I didn't really answer the "what" about mapguide4j (more like the "why"). So I'll answer that here.

mapguide4j, from a high level view can be thought of as a 100% Java version of the MapGuide Web Tier, which currently consists of:
  • The mapagent http interface.
  • The AJAX viewer
  • A REST interface to the MapGuide API
mapguide4j was a testbed for exploring ways to improve one of my perceived shortcomings of MapGuide: The ability to be a powerful geospatial web service platform. Sure we could've done this in C++ like GeoREST, but we get things done much quicker in a higher-level language like Java.

mapguide4j was built on top of the Play Framework (v2.0.4) which was chosen as the Java web application framework of choice for the following reasons:
  • It was easy to pick up and play (pun intended). The only required knowledge was Java. No need to understand Servlets, EJBs, JSPs, and every other Java technology/framework with a 3 letter acronym.
  • Simple installation and deployment.
  • Play uses its own fully self-contained high performance http web server, enabling us to have a fully portable and self-contained Web Tier in 100% Java, provided we had the ability to fully replicate the functionality of the mapagent http interface, which turned out to be true. No need to integrate with Apache or IIS, our Play-based mapguide4j can stand on its own with near zero configuration.
  • Play supports hosting within Apache/Tomcat if we need to use Apache/Tomcat as the front-end server.
mapguide4j is basically the current MapGuide Web Tier + lots of extra web services. Or so that was the plan.

So let's cover the various bits of the mapguide4j Web Tier.

mapagent/AJAX Viewer

This was the most crucial piece of the Web Tier. We needed a mapagent-compatible http interface so that client applications like Maestro can operate against mapguide4j with no modifications, and the quickest way to verify that we have a working mapagent is to bring across the AJAX viewer as well.

The first attempt at replicating the mapagent was to painstakingly map each http operation to its respective implementation (as I pictured it in my mind) using what's available in the MapGuide API. Of course, we then hit the problem of some key viewer operations that cannot be implemented this way. This was when I discovered the true purpose of MgHttpRequest and MgHttpResponse which then lit the proverbial light-bulb and made replicating the http mapagent dead simple.

The AJAX viewer was a simple transplant of the JSP code in the existing Java AJAX viewer to fit within the MVC paradigm of the Play Framework. It does 90% of what the existing AJAX viewer does (the missing 10% is esoteric commands and functionality that's not really used or not implemented, which anyone interested will have to fill in the blanks later on).

The end result is we have a 90% functional AJAX viewer within the Play Framework with a fully-compatible mapagent that can serve requests from existing http client applications like Maestro with little/no compatibility problems.

REST interface

This is where the bulk of the what's unique about mapguide4j lies in. The REST-ful interface is modeled on previous discussions about a theoretical REST-ful web service for MapGuide. If you're wondering if we're duplicating functionality that already exists in GeoREST, we're not actually.

GeoREST is about REST-ification of individual MapGuide Feature Sources and FDO connections. mapguide4j's REST interface is about the REST-ificiation of the mapagent endpoint itself, to be able to tap into a similar set of operations and services in a REST-ful manner through clean, well-defined URLs, where standard HTTP verbs (POST, GET, PUT, DELETE) conceptually map to creation/reading/updating/deletion of associated repository and feature data.

So to illustrate how this works we'll illustrate with the Sheboygan dataset. Here's the resource structure for reference


The mapguide4j REST interface resides under the URL of http://localhost:9000/mapguide/rest

All REST-ful URLs are relative to this base URL. All URL access require a session id parameter passed in or passing standard http authentication

The mapguide4j REST interface has several base URLs under the main base URL:
  • Site Repository access: http://localhost:9000/mapguide/rest/library
  • FDO Provider Registry: http://localhost:9000/mapguide/rest/providers
  • Coordinate System Catalog: http://localhost:9000/mapguide/rest/coordsys
  • Site Admin: http://localhost:9000/mapguide/rest/site
Example REST URLs

MapGuide resource ids already follow a pseudo URI syntax, so the restful URL to a given resource is simply tacking on the path component of the resource and then tacking on various suffixes depending on the desired representation we want back.

So for example, if we wanted a resource listing for a folder, we tack on a /list suffix which will return an XML response by default. For example, if we wanted to list the resources under the root directory, the request would be like this

GET http://localhost:9000/mapguide/rest/library/list

We can tack on specific file extensions for specific representations, so the above request can also be represented as

GET http://localhost:9000/mapguide/rest/library/list.xml



For JSON the request would be:

GET http://localhost:9000/mapguide/rest/library/list.json



List representations are also available in HTML

GET http://localhost:9000/mapguide/rest/library/list.html

It is through the HTML representation that allows us to have a basic rudimentary repository browser to navigate through the repository in the web browser



The HTML representation is also a convenient way to "explore" the other various representations as they are all conveniently hyperlinked for you.

For accessing individual resource content, we tack on a /content suffix. For example if we wanted the resource content of Library://Samples/Sheboygan/Data/Parcels.FeatureSource the request would look like

GET http://localhost:9000/mapguide/rest/library/Samples/Sheboygan/Data/Parcels.FeatureSource/content

Again, we can plug in the desired file extension for certain representations

GET http://localhost:9000/mapguide/rest/library/Samples/Sheboygan/Data/Parcels.FeatureSource/content.xml



GET http://localhost:9000/mapguide/rest/library/Samples/Sheboygan/Data/Parcels.FeatureSource/content.json



We'll stop right here because going through all the possible URLs would take forever. But hopefully this gives you the idea of what can be done through this REST interface. You can see example URLs through this link or through exploring the HTML representation list views that provides hyperlinked URLs to all the various supported representations.

Basically most resource and feature service APIs can be accessed via the REST interface

... and that's when everything stopped and ground to a halt.

I haven't actually touched this project in months, so rather than leave this project languishing into nothingness, I'm decided to re-purpose mapguide4j as a giant piece of sample code that should hopefully give you some ideas as to what you can do with the MapGuide API in Java.

You can check out mapguide4j at my GitHub. Unlike my other open source handiwork, I've picked the most liberal OSS license (MIT) I won't be doing anything more with this code. Feel free to fork/clone/hack it to your heart's content.

For me, I achieved what I wanted to know with mapguide4j. It proved to me that it was indeed possible to build some powerful web services on top of the MapGuide API and more importantly, to do it all in Java.

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
        conn.ResourceService.SaveResource(ldf)
        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 scale_range_utils.py 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.

Announcing: MapGuide Maestro 5.0 FINAL and 4.0.6 maintenance release

Nothing much to say here in terms of what's new.

Just some bug fixes from RC2, while 4.0.6 contains selective back-ported fixes and enhancements.

What I do have to say is that 4.0.6 will be the last maintenance release on the 4.0.x line. If you want stuff fixed, the source code is all there for you to get your hands dirty. I won't be doing any more releases on the 4.0.x line.

5.0.x will be the new stable/maintenance release from here on out.

Download

Tuesday, 9 July 2013

A little note about mg-desktop and packages.

Suppose you have done the bulk of your map authoring work for your mg-desktop based application on an actual MapGuide Server and then proceeded to bring this data across to your mg-desktop application via packages. When you load this package into your mg-desktop application's repository (via ApplyResourcePackage), this error gets thrown back at your face.



What does this mean? Isn't packages supported and implemented in mg-desktop?

You are indeed correct. Packages is supported. So what is going on? You might want to take a look at the package itself. Take a look at the MgResourcePackageManifest.xml file in the root of the package.



Does it have an XML fragment like this?



If so, here's the actual problem. Our package implementation in mg-desktop assumes that a package will mainly consist of a series of SETRESOURCE and SETRESOURCEDATA calls. If you package from the root level of the repository, it will insert fragments to do an UPDATEREPOSITORY call, whose API we have not implemented in mg-desktop. This is the MgNotImplementedException that is thrown back.

So what is the fix for this? Simply remove the XML fragment in question, save the modified package and reload it. It should now load properly.

Monday, 1 July 2013

Announcing: MapGuide Maestro 5.0 RC2

Here is the 2nd release candidate of MapGuide Maestro 5.0. No matching 4.0.x release with this one, that can wait when 5.0 final is out.

Here's some notable changes and fixes in this release.

We've made the new "Resampling Method" parameter in the GDAL Feature Source editor optional (indicated with a checkbox) because that parameter is actually optional and the UI should reflect that.


The Resampling Method value will only be applied if the check-box is checked.

The ODBC Feature Source editor gets support for secured credentials when using ODBC connection strings.


The "Apply Credentials" link only works when you specify the %MG_USERNAME% and %MG_PASSWORD% placeholder tokens somewhere in the connection string.


Click the "Apply Credentials" link then gives you the dialog to enter the actual username and password, which will be saved under the MG_USER_CREDENTIALS resource data


Once applied, the entered credentials will be used in place of %MG_USERNAME% and %MG_PASSWORD%, like all the other Feature Sources.

One last notable change is that the Map Definition editor allows you to re-order layers within a group in the "Layers and Groups" tab.


What does this actually do? The "unofficial" specification (that is followed by the Maestro, the AJAX Viewer and the Fusion Viewer) is that layers within a group are presented in their respective draw order. So in the above screenshot, "Parcels" is the bottom-most layer of the 3 layers and "Districts" is the top-most of the 3.

What moving a layer up/down does in this respect is move that layer up/down the draw order with respect to the other layers within that same group. So moving "Buildings" up means it will be above "Districts" and "Parcels" in the draw order, but layers in other groups may still be above "Buildings" in the draw order. This enabled function just guarantees that of the 3 layers in that group, "Buildings" will be the top-most one. Likewise when moving layers down.

This re-ordering only happens within a group. So if "Buildings" is the top-most layer of the group, you can't move it any higher. Similar thing applies for the bottom-most layer of the group.

This release includes some important bug fixes for MgCooker as well (thanks to dcreado for supplying the patches).

This will probably be the last release candidate before we go final.

Download