Wednesday, 17 August 2016

React-ing to the need for a modern MapGuide viewer (Part 4): A necessary detour

Continuing where we left off from last time, we had something that was closely approaching our existing AJAX viewer.

Sadly, I've hit an impasse.

This viewer has reached a point where I must take a momentary and necessary detour to accommodate a change of architecture to make further development work more manageable. At this point the viewer was a series of components connected together through various event handlers. The encapsulating application component was turning into a big ball of event handler spaghetti trying to get various components to talk to each other.

Any React application that grows in complexity will eventually encounter such problems, and when such problems arise, it necessitates the introduction of Flux architecture to centralize and streamline application state. So if we're going to introduce Flux into this viewer, we might as well go with the best: Redux.

What sells Redux for me as the top-tier Flux library of choice for React applications is its jaw-dropping developer tools support. Just check out this video for a taste of the developer experience we get with a React application using Redux.


Or for something more relatable, here's our current viewer redux-ified


All our viewer actions are now dispatched to a central redux store where various reducers are in place to update/replace various parts of the now centralized application state, and in combination with the Redux DevTools, we get a live audit trail of all our application actions and their respective data payloads and state transitions. Our various redux-ified components then respond to various changes in this redux store to update/render themselves.

You may also notice that we've got our iconic zoom navigator on our map now. Notice how it's properly flashing the loading animation as the map is loading/refreshing? This is because it's connected to a branch of the redux store where the map viewer component is dispatching Map/SET_BUSY_COUNT actions to whenever our MapGuide image sources are about to start/finish a map image request. A non-zero value means show the animation (as one or more rendering operations are in progress), a zero value means to hide it.

That is the extent of the "wiring up" that our components need. Dispatch to update state, connect to listen and re-render against updated state. Redux handles and coordinates the rest.

The Redux DevTools also has a nice graph view that allows us to see our centralized application state in a nice animated graph (which also live updates with each action and change of state)


Can you imagine this kind of developer experience with our current viewer offerings? Not a chance!

This tree (and the values within) is what entirely drives and describes the state of the whole viewer application. This is what a Flux architecture gives us. A single and centralized source of truth about our application.

Can you imagine as well, the ease of reproducing an issue in this redux-ified viewer? You just export the state tree with the Dev Tools, and send it to the developer. He/she can load import that state tree and replay the whole series of dispatched actions up to the point of failure.

The process of redux-ifying the whole map viewer is basically splitting our various components into "smart" and "dumb" versions. The dumb components simply takes the props given to it and renders itself from that, whereas the smart components are redux-aware, wrap the respective dumb component and passes new props to it whenever their connected state branches are updated.

This redux-ification is still in progress, meaning things like the Task Pane (and its AJAX viewer API emulation) are currently broken, but once it's done we can continue onwards with a solid, robust and more maintainable foundation in place.


Monday, 8 August 2016

React-ing to the need for a modern MapGuide viewer (Part 3): We almost have an AJAX viewer replacement

WARNING: This post is GIFs galore. You have been warned :)

When I last wrote about this viewer, we had the following:

  • A functional map viewer component based on OpenLayers
  • A working legend component
  • A mock Task Pane that will eventually function like its AJAX viewer and Fusion counterparts
So where are we at since that post? I think we have something that can almost replace the AJAX viewer.


The above screenshot may not fully cover the extent of changes, so let's cover the changes bit by bit.

External Base Layers

The map viewer can now have external layers incorporated. Currently, this can be OpenStreetMap or any XYZ-based tile set, like Stamen tiles as seen from the above screenshot.

One thing that will surprise you (as it did for me when I got this first working), is that there is no longer a hard EPSG:3857 requirement in order for your MapGuide Map Definitions to properly line up against OpenStreetMap and friends. The above screenshot shows our venerable Sheboygan sample map in its original EPSG:4326 coordinates lined up against EPSG:3857 stamen tiles.

This is possible due to client-side raster re-projection capabilities present in OpenLayers 3 itself. What this means is that as long as your Map Definition is in a coordinate system that has a corresponding EPSG code, OpenLayers will happily re-project other layers to line up with your Map Definition instead of vice versa. In the above screenshot, the Stamen tiles are being re-projected to line up with the Map Definition.

For maps that are not in EPSG:3857 or EPSG:4326, there is currently a small additional setup process to carry out.

For example if I load the map from the Melbourne sample dataset, the viewer throws this cryptic error message.


What this error means (and something I can hopefully clean up), is that OpenLayers has no idea about the projection EPSG:28355 (the projection of the map). Although the viewer includes proj4js and automatically wires it up to OpenLayers, it still only knows about two projections out of the box: 4326 and 3857.

Foreign projections have to be registered to proj4js first (epsg.io conveniently provides proj4js snippets to show you what to do) before mounting the map viewer application component. Once I registered the projection for EPSG:28355 to proj4js, the Melbourne map shows up properly, with OpenLayers happily re-projecting the EPSG:3857 stamen tiles to line up.



Toolbar

The viewer now contains a floating toolbar where we can dock our various commands to. Toolbars are completely data-driven and commands inside can be individually be selected/enabled/disabled based on various states of the map viewer.

The react-flyout component is used to provide support for flyout menus



Task Pane

In our previous post, the Task Pane was nothing more than a plain UI mockup. Now it is a mostly functional simulation of what is provided by our existing viewers.

The Task Pane functions as a generic content container where various viewer commands can show their UI in. In order to fully simulate the Task Pane, it needs to provide the JS functions that Task Pane content can call back into. In other words, our viewer needs to shim/emulate the AJAX viewer APIs to allow existing Task Pane content to work.

So to this end, I've been using the official PHP Developer Guide samples as a "reference implementation" for Task Pane content and the AJAX Viewer APIs they're trying to call. The page in the first screenshot is the landing page of the developer guide samples. So what this means now, is we have emulated AJAX viewer APIs for:

Zooming/Panning to a particular location


Setting selections


Refreshing the map in response to server-side layer/group changes


Digitizing geometry


And much more!

The AJAX viewer API emulation works so well, we can throw things like the existing AJAX Viewer buffer tool at it, and it will work just fine in our new viewer environment.


Legend

Finally, as can be inferred from previous screenshots, the Legend component is now aware of external base layers and provides a switcher UI if more than one external layer is present.


So when you combine all these changes together, I think I will get agreement here that we have something that nearly approaches (in some cases, exceed) the AJAX viewer in functionality. The only glaring omissions at this point are various display elements (like a status bar), reaching command parity with the AJAX viewer, and a layout/configuration abstraction akin to a WebLayout or ApplicationDefinition, which is something I intend to implement in some form.

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

The weight increase was mainly due to adding proj4js, but compared to Fusion we're still way smaller. As long as things stay under 1MB, I'd be content.

Saturday, 6 August 2016

MapGuide tidbits: mapguide-rest and PHP 5.6

Since MapGuide Open Source 3.1 Beta 2 shipped with PHP 5.6, some might be wondering if my mapguide-rest extension will work on this version of PHP (from a compatibility standpoint, we mainly are referring to the MapGuide API itself, which is the same as before, and not the surrounding Web Tier environment like PHP, Tomcat, etc).

The good news is: Yes, it works for the most part

I say that because, if you see a message like this:

Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version

It's because of some some deprecation warnings that are active in PHP 5.6. This message in particular, is due to the setting always_populate_raw_post_data not being disabled in php.ini.

And for another interesting fact? It's not set to 1, which should imply the setting is disabled right? Well you guessed wrong, it has to be explicitly set to -1 in php.ini for it to be disabled. Once disabled, these warning messages go away.

Another thing to look out for from the client-side is that PHP 5.6 may include charset in its Content-Type response headers. So if your client code was previously testing for application/json
exactly for a JSON response, that may fail now because PHP 5.6 will most likely send down application/json; charset=utf-8 instead. If you have such code, adjust accordingly.

But besides that, my mapguide-rest function test suite says things are A-OK with this release of PHP.

For a better out-of-the-box experience, we'll look to have this setting default to -1 in the php.ini we ship with MapGuide.

Sunday, 31 July 2016

Announcing: MapGuide Open Source 3.1 Beta 2

Here's the 2nd beta of MapGuide Open Source 3.1

The main change in this release is the "emergency" upgrade of the bundled PHP from 5.5 to 5.6.

This was done because the 5.5 series of PHP went end of life on July 10th. It wouldn't look good to ship the new version of MapGuide with a bundled version of PHP that is no longer supported. If you work with PHP in your MapGuide application, please observe any changes between 5.5 and 5.6 and migrate your application code accordingly (note that the MapGuide API is still the same here, the migration refers to any surrounding PHP code/libraries you may be using on top).

Besides the bundled PHP change, this beta includes the following changes:
The explicit property list changes are to allow tools like buffer/query/etc to better work against certain relational FDO providers where a "select * from table" query may "leak" out column types that the FDO provider may not properly handle (and would normally be hidden in any class definitions returned by the provider)

500,000 pageviews!

Hey! It's another blog milestone!

Thanks for the continued eyeball patronage!


Friday, 22 July 2016

React-ing to the need for a modern MapGuide viewer (Part 2): The baseline

I've decided that cataloging my adventure in developing this viewer is enough to warrant its own blog mini-series. So here's part 2 of my adventure. I have no idea how many parts this will take :)

So to continue where we left off, we had a basic map viewer component built on the main pillars of
And it is all written in glorious TypeScript. I've mentioned once on a previous post that writing web frontend code in a language with static typing and a compilation phase (gasp!), with a clean and simple-to-understand component model offered by React is just heavenly! If I could go back in time, I'd have written the Fusion framework in TypeScript, but sadly it didn't exist around 2008 (when Fusion made its debut in MapGuide) and my involvement in the MapGuide project was nowhere near the level it is now.

So before I continue, I should also probably mention what I aim to achieve with this viewer:
  • It will be built on modern, best-of-breed web front-end technologies (React, OpenLayers 3, etc)
  • I will not be shackled with the burden of supporting legacy browsers. This viewer will demand a modern web browser. If one has to use Internet Explorer, it must be IE11
  • It will require MapGuide Open Source 3.0 as the minimal version as this viewer will leverage capabilities present in this particular version onwards.
  • It will at least have some level of feature parity with the AJAX/Fusion viewer offerings, that is to say:
    • It will have a Legend component for toggling layer visibility and comprehending layer symbology/thematics.
    • It will have a Task Pane component that functions as a generic container for content and/or contextual UI
    • It will have a Map Selection component for easy viewing of selected features
    • It will include a stock set of commands, with extension points for developers to add their own.
    • It will have opt-in integration with external base layers (OpenStreetMap, etc), provided your Map Definition meets the requirements (ie. It is in EPSG:3857/WGS84.PseudoMercator).
    • I will not at this time consider any kind of integration with Google Maps as their APIs are not only a constantly moving target, but a TOS minefield I just do not want to wander in at this point in time.
    • I do hope to allow for some level of free-form component layout (ala. templates) so one is not tied to a layout I choose by default.
So with that being said, for this post I want to focus on the last major bullet point: Getting to parity with our current viewer offerings.

So let's introduce our new Legend component.


Implementing this component wasn't difficult, I mainly copypasta'd most of the presentation logic from the existing feature-complete legend used in my various mapguide-rest sample applications.

The difference here is that because this is now a React component, there is zero jQuery or DOM manipulation. Instead, we are just render Group/Layer child components for each layer and group we encounter in our runtime map. It is all pure UI as a function of props and state, and letting React's virtual DOM figure out how to actually update the DOM in the most efficient manner. What this means is that our component model cleanly matches how our layers and groups are conceptually structured.


As for the other components, I'm still working on them (currently focused on the Task Pane) so we'll leave that for another post. But visually speaking, when you put these 3 items together:


It's already starting to look like our existing AJAX viewer!

And before I close out this post, In terms of our current JS bundle size, here's the results of the current weigh-in (using our current Fusion production bundle for comparison).



We've got plenty of breathing room.

Saturday, 16 July 2016

React-ing to the need for a modern MapGuide viewer

A few months ago, I mused about a hypothetical future map viewer for MapGuide.

Having a good solid 6 months of working with React and TypeScript (and dealing with Webpack in anger) under my belt, I felt confident enough to give this idea a crack.

This is what the consumption story looks like so far:


The HTML is just:
  • One script tag (to the webpack bundle, containing the map viewer component and all of its dependencies
  • Making an instance of the entry point class (currently called MapGuide.Application)
  • Mounting the class (using React terminology here) at the desired DOM element with properties to pass into the root React component
And voila! Instant OpenLayers3-powered map viewer. Pretty simple stuff.

When we look at the current React component composition:



It's one root component (I'm intending for this to be something conceptually similar to the ApplicationDefinition/WebLayout for our existing viewers), and a child component that is wrapped by a react-dimensions higher-order component to automatically resize the map viewer according to parent element/window size changes (so we can hook in and call updateSize on the internal ol.Map to prevent image distortion)

The major deviation from my original thoughts is that in the above HTML code, there is no references to React or having to mount the viewer component using the React APIs. I've come to realize that this would be cumbersome as your ultimate entry point will always be HTML/JavaScript, way outside the zone of React/TypeScript/(T|J)SX, and although this is a "component", our final bundle is ultimately more an application instead of a library. So having a wrapper (which is what MapGuide.Application does) to do all of this leg work simplifies the whole setup process.

That's not to say we can't componentize this map viewer (as a library/package you can npm install), but my primary goal is to have an experience similar to our existing viewer offerings, which are applications whose features and behaviour is driven by configuration files. So in that respect, including a script tag and writing some JavaScript to point to your configuration and kickstart the application is much more easier to comprehend than installing node, npm, Webpack and messing with frontend development stacks.

So far so good. We're just beginning to scratch the surface.