Tuesday 27 September 2016

Introducing mapguide-react-layout. A modern viewer for MapGuide

Let's recap the story thus far:
Up until now, I have intentionally withheld the GitHub repo where all this development action was taking place as I wasn't going to reveal something that I didn't think was ready to be revealed. Well, it's now reached a point where I am comfortable with finally unveiling this project for general public consumption.

I'm proud to introduce mapguide-react-layout, the new modern map viewer for MapGuide Open Source and Autodesk Infrastructure Map Server.

mapguide-react-layout requires at a minimum MapGuide Open Source 3.0 or an equivalent version of Autodesk Infrastructure Map Server. As I've stated in part 2 of this series, this viewer will require a modern web browser, which practically speaking is any of the following:
  • Google Chrome (stable channel)
  • Mozilla Firefox (stable channel)
  • Internet Explorer 11. Older versions of IE will not be supported
  • Microsoft Edge
  • For iOS: Mobile Safari
  • For Android: Google Chrome or Mozilla Firefox
Overview

Conceptually speaking, you can think of mapguide-react-layout as the logical successor to Fusion and the AJAX viewer and it carries similar high-level concepts as well:
  • mapguide-react-layout has templates just like Fusion.
  • Your application consists of various components (as opposed to widgets in Fusion)
  • Components subscribe and dispatch actions to push data to a centralized redux store for application state. (as opposed to widgets having to explicitly subscribing to each other with event handler spaghetti). Application state is significantly easier to reason about in mapguide-react-layout as opposed to Fusion or the AJAX viewer.
  • mapguide-react-layout offers the same extension points for custom functionality for your MapGuide applications, such as InvokeURL commands.
  • Just like before, a mapguide-react-layout viewer is driven by a Web Layout or a Application Definition (this is not implemented at this stage, but is something I intend to complete).
It is the last point which is probably the most salient. Because a mapguide-react-layout viewer accepts a Web Layout or Application Definition, the authoring and development workflow for MapGuide applications using this viewer remains unchanged

Instead of pointing a Web Layout to an AJAX viewer URL or an Application Definition to a Fusion template URL, you just point either resource to your mapguide-react-layout viewer.

Templates

The mapguide-react-layout viewer includes the following layout templates out of the box:

1. AJAX Viewer



This template visually mimics the original MapGuide AJAX viewer (with much less frames!)

2. Sidebar


This template is a responsive layout based on the sidebar-v2 map viewer template. Well suited for phone/tablet sized displays.

In future releases (once I get the Application Definition support implemented), I'll look at bringing across the existing 5 Fusion templates as well.

Getting the viewer/code

The code to mapguide-react-layout can be found on my GitHub repo. There you will also find installation instructions and other various documentation/notes.

Feedback, questions and pull requests welcome.

Saturday 24 September 2016

Announcing: MapGuide Open Source 3.1 RC1

Here's the first release candidate of MapGuide Open Source 3.1

Of the fixes made since the Beta 2 release, the most notable will probably be that the installer should now work properly on Windows 10. Didn't this work in the past? Yes it did, but when it did work, Windows 10 was still in preview and the version of IIS was most likely not the version it is now (10.0).

Why is this important? Because the windows installer does an IIS version check to determine what screens to present in the Windows Installer UI. Our version check was:

IISVERSIONMAJOR >= "#7"

Which basically says: show the IIS-specific UIs (and allow advancing through the IIS-specific screens) if the Windows Installer detected IIS (version 7 or higher) on the host machine. On Windows 10, the version reported is "#10".

And due to how lexicographical string comparisons generally work, the above check fails because "#10" is not greater than "#7". Ain't Microsoft versioning great? They skipped Windows 9 to avoid a whole class of version checking problems, but as we've found out some still slip through the cracks. The IIS installer check is now:

(NOT IISVERSIONMAJOR = "#0")

Which just means show the IIS-specific installer UI screens if IIS is present. We don't care what version it is, and "#0" signifies that IIS is not installed. 

And the reason we don't have to care about the particular version is because the versions of Windows that our installer supports will have IIS 7 or higher. So unless Microsoft decides to move away from an appcmd.exe-based approach to IIS configuration in the future, the Windows installer shouldn't have anymore IIS-related issues in the foreseeable future.

So with that interesting version story out of the way, here's the binaries at the usual place.

Wednesday 7 September 2016

MapGuide tidbits: Turning selection XML into attributes or selection images via the mapagent

Since MapGuide Open Source 2.6, the mapagent has an improved QUERYMAPFEATURES, which allows for greater flexibility to handle the selection and tooltip requirements of our various client-side map viewers. Previously, you would've needed custom server-side code using the MapGuide Web API to achieve this, but now that it is part of the mapagent, all you need is a basic AJAX request.

The main purpose of this operation is to produce an encoded feature selection based on an input geometry (from a mouse click or a box drag) or depending on what you ask for, it can also include:

  • An inline base64 selection image (to avoid a subsequent selection image rendering request)
  • Tooltip/Hyperlink information
  • Attributes and bounding boxes of the selected features
Now what some of you may not know is that you can also use QUERYMAPFEATURES to pass in existing selection XML to get back the inline selection / tooltip / hyperlink / attributes. To do this, you have to omit the GEOMETRY parameter (which normally has the input WKT geometry from the mouse click or box drag) and specify a FEATUREFILTER parameter instead which contains your selection XML. Yes, it's a confusing name. You would've thought it would be named SELECTIONXML or something like that wouldn't you?

Now there is one important note about using QUERYMAPFEATURES in this manner, and was something that I originally thought was a critical bug, but is actually a false positive (hence my motivation for this post): Make sure to include a LAYERATTRIBUTEFILTER with a value of 0.

The LAYERATTRIBUTEFILTER parameter is a bitmask that applies layer-based restrictions on what part of the selection to render:
  • 1 = Include visible layers
  • 2 = Include selectable layers
  • 4 = Include layers with tooltips defined
When the LAYERATTRIBUTEFILTER is not specified, MapGuide will internally default to the value of 3 (include visible and selectable layers). When you combine this with passing up selection XML instead of an input geometry, can you see why it may raise a false alarm? Because your selection XML may be for selected features that are not visible in your current view, so when you pass the selection XML up to QUERYMAPFEATURES without setting LAYERATTRIBUTEFILTER=0, MapGuide will use default visibility/selectability criteria and most likely omit your selected features from the selection image / tooltip / hyperlink / attributes and as a result, you may get back nothing despite passing up valid selection XML and asking for selection image / tooltip / hyperlink / attributes.

So if in doubt, if you're passing up selection XML to QUERYMAPFEATURES, be sure to pass up LAYERATTRIBUTEFILTER=0 as well to avoid any nasty surprises.

Saturday 3 September 2016

React-ing to the need for a modern MapGuide viewer (Part 5): Back on track and racing

Previously in this series, I had to take a momentary detour to restructure the viewer to use Redux for streamlined application state management as the viewer has grown to the point where having a Flux architecture was essential for viewer development to continue at a maintainable level.

The challenging part of this process was to get the map viewer component to play "dumb" (so that we can wrap a "smart" redux-aware presentational component around it), namely because this component is the one that wraps OpenLayers, and we had to modify our usage in a way that facilitates uni-directional data flow that is required by Redux. Instead of making direct calls to OpenLayers to pan/zoom/etc, it now pans/zooms/etc based on the component props and any changes to those props.

This presents a slightly awkward at first glance (but necessary) situation where any events from OpenLayers that would change the map view and display parameters, we have to intercept these changes and flow it back to the Redux store first, that would then propagate back to its "smart" parent component that will set the necessary prop changes to carry out the map actions.

This work is now mostly complete, so now we can focus back on continuing evolving the viewer.

As I've been implementing the following features described below, I've gotten this great feeling of validation in my choice of using React, because there is actually high conceptual synergy between React and our current Fusion viewer framework.
  • Fusion is based on widgets. React is based on components
  • A Fusion application is composed by widgets described by a Flexible Layout document. Our react-based viewer is composed by various components, which I intend to compose together through a configuration abstraction similar to a Web/Flexible Layout document in combination with our (now) centralized application state in the Redux store.
But unlike Fusion, we have a single source of truth for application state. In Fusion, application state is all over the place with some state requiring manual event subscription to be notified of any changes. Our react-based viewer has none of these problems thanks to Redux.

So now, here's what I've achieved since the last post in this series.

Layout Templates

We now have the notion of layout template components. This is the Fusion template analogue ported over to our React-based viewer. To support this template concept in the React world, we need a supporting cast:

1. We have various registry modules that allow various components and commands to be registered. This is also an extension point that allows for external components and commands to be registered as well.

2. We also have a notion of "placeholder" components which reference a particular component by its registered id. Layout template components consists of these placeholder components and various layout/placement code, which once the application has been initialized, these placeholder components will mount and will render the actual components from the registry by their component ids. The placeholder allows for a clean error boundary that allows us to show informative errors in its place should we try to render a component that has not been registered.

In practice what placeholder components give us can be illustrated like so:

Here's the viewer using the "AJAX Viewer" layout.



Should some components not be registered, say the Task Pane and Legend components, we get a clear error message that spells it all out.


In Fusion, such a situation would probably be a silent failure. Even in this state, the new viewer is still mostly functional as all these components are now independent of each other. Their only shared dependency is the centralized redux store. So with this clean separation of components and presentational layout now in place, I've also explored some other template designs. In particular, a port of my dream responsive map viewer layout.

Say hello to the "Sidebar" template, named after the sidebar-v2 template it was originally derived from:


Unlike the original template, all the jQuery that drives the original sidebar behavior has been ripped out and replaced with React components that do the same thing. Also it has some extra niceties, such as the main toolbar docked vertically, and some in-built smarts to auto-flyout Task Pane content when URL commands are invoked, which is important on mobile devices, as illustrated below when I try to do a buffer on an emulated smartphone display.


Web Layout compatibility

Here's something about the two templates above. They are both driven by the same Web Layout document. Why invent a new configuration mechanism when our existing Web Layout and Flexible Layout documents already do this for us?

Being able to support Web Layouts (and in the future, Flexible Layouts) is important as it means the authoring process for MapGuide applications using this new viewer remains unchanged. The only difference is you point the Web/Flexible Layout to this viewer instead of your AJAX/Fusion viewer. Now this support is not 100% compatible, there's still missing commands I've yet to port across, but feed a Web Layout document to this viewer and it should be able to recognize most of the commands and configuration options within.

As hinted in the previous paragraph, I intend to support Flexible Layouts as well. As mentioned previously, Fusion has such conceptual similarity to React that most of the things that drive Fusion should be easily portable to our new viewer as well.

Other cool stuff

Thanks to ol3-contextmenu, we got a functional context menu now. This is driven by context menu settings from the Web Layout.



The viewer now has basic modal dialog support as well, which is an important component for an "Aqua" style layout template when I get round to it, and also as an alternative target for Invoke URL command content.


Current weigh-in

Before I close out this post, here's the current weigh-in for our viewer bundle.


I have no doubt we'll cross the 1MB barrier soon, but once I start seriously looking at "paying for only what we use" with regards to all the libraries we're using, the final bundle size should hopefully approach something more reasonable