Friday, 16 March 2018

A TypeScript-powered moment of clarity

Today was a case where I forgot about one of the major strengths of TypeScript: The type-safety that it provides you and the features available to allow for writing code with maximum type-safety.

Consider this simple utility function in TypeScript

export const strIsNullOrEmpty(str: string | null | undefined): boolean {
    return str == null || str == "";

The function does what it says on the tin: Checks if the given string is null (or undefined) or an empty string and returns true if that's the case or false otherwise.

There's no problems with using this function, until you turn on null checks or use this function in a TS code-base with null checks enabled.

And if I hover over the red squiggly, I see this:

But how is that possible? I know that by calling this function, there's no way that the variable str could be null or undefined.

This is indeed the case ... at runtime.

At compile-time TypeScript does not know that. So short of spamming exclamation points all over your code, how can we tell TypeScript that the string we passed in cannot be null or undefined if we just checked for it?

The key is to change the function from the above example into this:

export function strIsNullOrEmpty(str: string | null | undefined): str is null | undefined {
    return str == null || str == "";

And the red squiggle goes away.

This is feature of TypeScript known as type guards. It's a way to write functions that evaluate to true/false at runtime, but at compile time allows you to make some assertions about the type of the variable you passed in to the TypeScript compiler. In our case, passing in a variable that could be a string or null or undefined has to be either null or undefined if this function returns true. If it returns false, then it has to be a non-null string, which TypeScript knows then to strip of the null | undefined on any subsequent if block following that function call. This is what's known in TypeScript as type narrowing.

Type guards have been in TypeScript for a long time, but it took today for me to realise that some of my utility functions (like this example) should've been written as type guards, especially with the advent of TypeScript 2.0 and its powerful null checking capabilities.

Now to double check places in my code where I've been sprinkling ! operators all over the place.

Wednesday, 21 February 2018

Announcing: mapguide-react-layout 0.11

Here's a long overdue release of mapguide-react-layout with a ton of new features.

GIFs and screenshots galore below. Brace yourselves!

Adding external WMS layers

A new component is available to easily add external WMS layers to your map.

As previously mentioned, you can access this component through a InvokeURL command that uses a component://AddManageLayers URI instead of a normal URL.

Partial Application State now encoded in URL

Through the use of react-url-query, we now have the ability to transfer part of our internal application state into the URL, allowing us to reload the browser window and be able to resume from where we left off.

The following bits of application state are now encoded into the URL:
  • Current view (x/y/scale)
  • Current active map (for multiple map configurations)
  • Shown/Hidden layer and group names
  • Current session id
Share Link to View

A new component is available to easily share this newly stateful URLs to other users.

Once again, you can access this new feature through an InvokeURL that has a component://ShareLinkToView URI instead of a normal URL. The session id is omitted by default, but you can tick Include Session ID to include your session id

Measure Segment Display

This was a feature missing from the equivalent Fusion widget that has finally been ported over. The measure tool properly displays individual measured segments.

Segments are only displayed for geodesic measurements. 

Drawing/Digitization improvements

Digitization now supports easily undoing the most recently drawn point by pressing the 'U' key.

This key and the key for cancelling the digitization (ESC) can be bound to different keys through new viewer mount options.

Template Improvements

The ajax-viewer template has been tweaked to look more like the original

This is possible because of the use of react-splitter-layout that gives us resizable panels.

The slate, maroon, limegold and turquoiseyellow templates also use this new component to finally give us resizable sidebars.

HTML property values in Selection Panel

Through new mount options, you can specify property values that are HTML to be presented as actual HTML, meaning property values that used to look like this:

Now looks like this:

This feature is disabled by default and you must opt into it (through new mount options). If the HTML content you're presenting is potentially un-trusted you can provide a sanitization function to clean the provided content (for example, with DOMPurify) to guard against possible cross-site scripting attacks.

Other Changes
  • Updated React to 16.2
  • Updated Blueprint to 1.35.5
  • Updated OpenLayers to 4.6.4
  • Updated TypeScript to 2.7.2
  • Debug viewer bundle (viewer-debug.js) now included
  • Added support for CenterSelection fusion widget. This marks the end of the Fusion widget porting work. No further Fusion widgets will be ported across. The list of Fusion widgets that will not be ported across can be found here.
  • Added support for extra extension properties in Redline and SelectPolygon widgets
  • Flyouts are now mutually exclusive, behaving more like their Fusion counterparts
  • Broke/404 toolbar/menu icons now gracefully show error icon as a placeholder.
  • Added support for manual feature tooltip toggling. This replaces click-based map selection if active and can be controlled through the Viewer Options UI.
  • Fix: Initial view of the map not using full viewport.
  • Fix: Commands now fall back to running in modal dialog if Task Pane is not present.
  • AJAX map frame viewer API is now fully emulated.

Project Home Page
mapguide-react-layout on npm

React-ing to the need for a modern MapGuide viewer (Part 20): It doesn't have to *specifically* be a URL

Wow! It's been 5 months since the last post related to mapguide-react-layout.

No I haven't stopped development. There's still plenty of things I want to achieve with this project before I consider it "done". It just had to take a back seat to other things that needed some attention:
Now that things have calmed down a bit, before I talk about the new release of mapguide-react-layout that will drop real soon, I want to talk about a capability of mapguide-react-layout that has been present for some time now, but I want to dedicate this entire post to because understanding this capability is the key in knowing how we can add new features to mapguide-react-layout yet retain the ability to use our existing Web Layout and Application Definitions to reference such new features.

I'm sure most of you should already be familiar with InvokeURL commands. You can define these commands in both a Web Layout and an Application Definition. They get represented in the AJAX/Fusion viewer as toolbar or menu items. When you click them, they invoke the specified URL into a given target (the Task Pane, a new window or a specific frame). InvokeURL commands is the basic gateway to custom MapGuide viewer functionality.

InvokeURL commands are also supported in mapguide-react-layout, except we can do extra things with such commands in mapguide-react-layout by leveraging the fact that a URL is merely a type of URI (Uniform Resource Identifier) and a URI ... does not necessarily have to start with http://. MapGuide resource identifiers (eg. Library://Foo/Bar.MapDefinition) are basically like URIs so this shouldn't be too much of a foreign concept.

So what does mapguide-react-layout do with this fact?

To demonstrate, here's a new feature that will be in the next release of mapguide-react-layout: A component to easily add external WMS layers

How can you access this new feature? Through an InvokeURL command.

Except, you don't invoke a URL you invoke a URI ... of our own custom creation.

As you can see from the above screenshot, we've invented our own component:// URI scheme to easily tap into new features in mapguide-react-layout without any structural changes required in the Web Layout or Application Definition schemas. We just leverage the existing InvokeURL command support and leverage the fact that whatever we're invoking in mapguide-react-layout ... doesn't necessarily have to be a URL. component URIs are structured as follows:

component://[component name][?query]

A component name refers to any component that's registered in the component registry. If a component can take parameters, you can pass them through the query string. Several components are available in the standard viewer bundle. The list of accessible components is available here.

If you use the npm module to roll your own custom viewer bundle, we provide APIs to allow you to easily create your own custom components. This is demonstrated in this example project.

The list of components available is small so far, that will grow over time as we add new features in future releases of mapguide-react-layout and the best thing about this is that to take advantage of such features, no changes to any authoring tools is required. You just have to enter a different kind of URI instead of a URL.

Saturday, 10 February 2018

One MapGuide/FDO build system to rule them all?

Before the bombshell of the death of Autodesk Infrastructure Map Server was dropped, I was putting the final finishing touches of making (pun intended) the MapGuide/FDO build experience on Linux a more pleasant experience.

Namely, I had it with autotools (some may describe it as autohell) as the way to build MapGuide/FDO on Linux. For FDO, this was the original motivation for introducing CMake as an alternative. CMake is a more pleasant way to build software on Linux over autotools because:
  1. CMake builds your sources outside of the source tree. If you're doing development on a SVN working copy this is an absolute boon as it means when it comes time to commit any changes, you don't have to deal with sifting through tons of autotools-generated junk that is left in your actual SVN wc.
  2. CMake builds are faster than their autotools counterpart.
  3. It is much easier to find and consume external libraries with CMake than it is through autotools, which makes build times faster because we can just source system-installed copies of thirdparty libraries we use, instead of waste time having to build these copies (in our internal thirdparty source tree) ourselves. If we are able to use system-installed copies of libraries when building FDO, then we can take advantage of SVN sparse checkouts and be able to skip downloading whole chunks of thirdparty library sources that we never have to build!
Sadly, while this sounds nice in theory, the CMake way to build FDO had fallen into a state of disrepair. My distaste for autotools was sufficient motivation to get the CMake build back into working condition. Several weeks of bashing at various CMakeLists.txt files later, the FDO CMake build was operational again and had some several major advantages over the autotools build (in addition to what was already mentioned):
  • We can setup the CMake to generate build configurations for Ninja instead of standard make. A ninja-powered CMake build is faster than standard make ^.
  • On Ubuntu 14.04 LTS (the current Ubuntu version we're targeting), all the thirdparty libraries we use were available for us to apt-get install in the right version ranges, and the CMake build can take advantage of all of them. Not a single internal thirdparty library copy needs to be built!
  • We can easily light up compiler features like AddressSanitizer and linking with the faster gold instead of ld. AddressSanitizer in particular easily helped us catch some issues that have flew under the radar.
  • All of the unit tests are build-able and more importantly ... executable outside the source tree, making it easier to fix up whatever was failing.
Although we now had a functional FDO CMake build. MapGuide still was built on Linux using autotools. So for the same reasons and motivations, I started the process of introducing CMake to the MapGuide build system for Linux.

Unlike FDO, MapGuide still needed some of the internal thirdparty libraries built.
  • DBXML - No ubuntu package available, though we can get it to build against a system-provided version of xerces, so we can at least skip building that part of DBXML.
  • Apache HTTPD - Ubuntu package available, but having MapGuide be able to integrate with an Ubuntu-provided httpd installation was not in the scope of this work, even though this is a nice thing to have.
  • PHP - Same reasons as Apache HTTPD
  • Antigrain Geometry - No ubuntu package available. Also the AGG sources are basically wedded to our Renderers project anyways.
  • DWF Toolkit - No ubuntu package available
  • CS-Map - No ubuntu package available
For everything else, Ubuntu provided the package in the right version ranges for CMake to take advantage of. Another few weeks of bashing various CMakeLists.txt files into shape and we had FDO and MapGuide both build-able on Linux via CMake. To solve the problem of still needing to build some internal thirdparty libs, but still be able to retain the CMake quality of everything is built outside the source tree, some wrapper shell scripts are provided that will copy applicable thirdparty library sources out of the current directory, build them in their copied directories and then invoke CMake and pass in all the required parameters so that it will know where to look for the internal libraries to link against when it comes to build MapGuide proper.

This was also backported to FDO, so that on distros where we do not have all our required thirdparty libraries available, we can selectively build internal copies and be able to find/link the rest, and have CMake take care of all of that for us.

So what's with the title of this post?

Remember when I wrote about how interesting vcpkg was?

What is best used with vcpkg to easily consume thirdparty libraries on Windows? Why CMake of course! Now building MapGuide on Windows via CMake is not on the immediate horizon. We'll still be maintaining Visual Studio project files by hand (instead of auto-generating them with CMake) for the immediate future, but can you imagine being able to build FDO and MapGuide on both Windows and Linux with CMake and not have to waste time on huge SVN checkouts and building thirdparty libraries? That future is starting to look real possible now!

For the next major release of MapGuide Open Source, it is my plan to use CMake over autotools as the way to build both MapGuide and FDO on Linux.

^ Well, the ninja-powered CMake build used to be blazing fast until Meltdown and Spectre happened. My dev environment got the OS security patches and whatever build performance gains that were made through ninja and CMake were instantly wiped out and we were back to square one in terms of build time. Still, the autotools build performed worse after the meltdown patches, so while CMake still beats the autotools build in terms of build time, we ultimately gained nothing on this front.

Thanks Intel!!!

Thursday, 11 January 2018

RIP: Autodesk Infrastructure Map Server (2006-2018)

Prepare the burial plot in the Autodesk Graveyard, the news has come out which I had long suspected, but is now official: Autodesk has ceased development of Infrastructure Map Server, the commercial counterpart of MapGuide Open Source.

However unlike Autodesk's other ill-fated products, Infrastructure Map Server has the unique lifeline of being able to live on through the MapGuide Open Source project because AIMS is built on top of MapGuide Open Source. Just because AIMS has died does not mean the same fate has to apply to MapGuide Open Source. This project has been in existence for 12 years and counting. The future of the MapGuide Open Source project can be as bright as the community allows for it.

If you are an AIMS customer wondering what your options are in light of this announcement, you should subscribe to the mapguide-users mailing list (if you haven't already) and share your thoughts, questions and concerns.

If you provide support/consulting/development for MapGuide/AIMS you should also subscribe and advertise your services.

I'll make some announcements on the mailing lists about future plans for MapGuide Open Source.

Rest in peace Autodesk Infrastructure Map Server, formerly known as MapGuide Enterprise (2006 - 2018)