MapStore 2: Modern WebMapping with React
First of all this is going to be a long blog post, so be prepared. We are going to talk about the work we are doing to create the new version of our webmapping open source product, MapStore. Although the product is not ready to be released there is quite some good experience that we can share so we thought it was already worth sharing it on our blog.
Btw, if you are like me and prefer pictures over words skip at the end of the post to see some action!
MapStore 1.x: the ugly, the bad and the good
MapStore 1.x is a nice WebGis framework due to its pluggable architecture, and its coherent and familiar look & feel.
Unfortunately its pillars (OpenLayers and ExtJS) represents both its strength and its weakness. From them MapStore inherited the ease of extension and many powerful widgets, but also the difficulty to progressively evolve to embrace new emerging needs, for example a more modern look & feel and new features coming from newer mapping libraries.
MapStore was built using a monolithic approach, fully depending on ExtJS for the look & feel and the application infrastructure, and also strictly bound to OpenLayers for the mapping capabilities, through the adoption of GeoExt and gxp as an additional layer of dependency. Eventually we were at a point where we could not think to plan any important improvement without rethinking the overall MapStore architecture from the ground.
We had two alternatives: remain in the comfort zone and upgrade MapStore to new versions of ExtJS (6.0) and OpenLayers (3), eventually contributing to the GeoExt project to support them, or take the risk to choose a new set of technologies and build a completely new version of MapStore based on them. This forced us to get out of the cretaceous era of web development and discover a new world that was emerging around us.
- At the beginning there was jQuery, the catch’em all DOM manipulating mini library, that was good for any small to medium website development
On the side, a new set of tools is now available that mimic the functionalities of our beloved Java server-side tools for code and project management:
- Npm → maven
- Gulp/Grunt → ant
MapStore 2: A Pinch of This, A Handful of That
We had to decide what to do with the next MapStore version and definitely, there was a lot to choose from.
Moving onto using newer versions of ExtJS and GeoExt was a no go from the beginning: we have been hurt enough already by the monolithic approach of ExtJS, and we wanted a simpler solution.
The world of web development is looking for more simplicity to handle complexity (whether this has been achieved or not is hard to say though…). We were looking for smaller libraries with a specific purpose that could be replaced with a smaller effort than the full ExtJS. We were also trying not to choose a single mapping library for any possible application.
Let’s explore the options.
- OpenLayers 3: a complete mapping library with many functionalities and controls, on the heavy side, with a renewed, canvas based rendering engine
- LeafletJS: a light and simple mapping library, with a pluggable architecture
- CesiumJS: the earth is an oblate sphere, why should we make flat boring maps?
Basic JS framework / libraries
- AngularJS 1.x: the most promising framework with both a web component architecture (through directives) and a data flow infrastructure, based on two way data binding; modularity is handled mainly by dependency injection. Seems a little bit complex, and AngularJS 2.x is coming and it’s announced to be incompatible with version 1
- EmberJS: MVC based, with interchangeable template system and based on conventions. Seems a little bit immature, it’s not clear what is going to be in the future, probably too rapidly evolving
- ReactJS: opinionated library to build the view of a web application, some concerning choices (JSX, Virtual DOM, no clear architectural choice)
The world of web development is rapidly moving, but we had to take a decision: we had to choose something that was not going (at least in our hopes) away in a couple of years, and if it did, we could plan the switch in steps, instead of throwing everything away like we are doing now.
Both AngularJS and EmberJS seemed too fast moving targets, with upcoming, revolutionary, 2.x versions at the door, and the underlying complexity of AngularJS was a little bit intimidating to us. ReactJS, on the other side was simple enough and didn’t take care of the overall application, concentrating on the view aspect, through the composition of small components.
We could choose ReactJS and use something else for other parts of the application: data flow, state management, web services interaction, etc.
We also didn’t want to choose a single mapping library, but rather trying to be compatible at least with OpenLayers 3 and LeafletJS leaving room for the integration of other libraries in the future. The component architecture of ReactJS allows this, by building interchangeable components, abstracting the underlying mapping implementation. So, the choice was to choose as little as possible, at least from the beginning.
We started building a set of core components, using ReactJS, like Map, MousePosition, TOC, etc. clearly distinguishing the ones strictly depending from the mapping libraries and the ones that do not depend on them (so that we can easily switch the mapping library as needed). The components depending from the mapping library were implemented both for OpenLayers 3 and LeafletJS, so that:
- they shared the same interface (the set of component properties and their meaning), to be interchangeable
- they were independent from other components, to avoid duplication
It was also important to us that the components depending strictly on the underlying mapping library were taken to the bare minimum (mostly the Map component(s)) in order to limit the coupling with it.
When the first components were ready, we needed to build a little bit of infrastructure, and we started choosing from one of the several implementations of Flux (the one-way-dataflow architecture backed by Facebook). First of all, did we need Flux? For sure we needed something to write the glue code that could made the ReactJS components communicate.
In that precise moment a new library was emerging from the crowd of Flux implementations. Its name is Redux, and it’s not a ReactJS only technology.
Redux is about simplifying state management through a set of principles:
- Single source of truth
- Immutability of the state
- Pure functions to describe state transitions
It looked like the perfect match for our need of simplicity, so we adopted it right away, adding actions, reducers and a store to our components, so that they could be made into a real application, and our first examples were born.
Quality, quality and also quality
For MapStore 2 we wanted to focus on superior quality for the code with respect to the previous version. This basically meant:
- Adding unit testing for components, actions and reducers
- Adding a build process based on npm and webpack, integrated with server-side through maven (and this is probably the part that still needs more simplicity)
- Add continuous build (using Travis CI) and deploy (using Jenkins) on the demo server where manual testing is performed after each deploy
- Nobody should commit directly without a peer review, hence PR are the suggested way of contributing
- We use codacy to control code quality
- We use coveralls to enforce at least 80% code coverage
Have a look at the project on GitHub. We are open to suggestions for improvement…
MapStore 2: we are almost there…
Ok, enough talking let’s see some pictures to show what we have done so far. The code is Open Source (License is Simplified BSD) and is available on GitHub. The current demo page is here and the continuous build and deploy with Jenkins is here. Let’s dig a little into the various widgets and components.
We have built a minimal viewer application with a set of widgets for both LeafletJS and OpenLayers 3 (can you spot the difference?).
We have experimented with a specific layout for mobile, that kicks-in automatically when the application is used from a mobile device.
We have built a 3D viewer based on CesiumJS.
We have created a full set of basic components like: LocateMe, IdentifyOnMap, TableOfContents, BackgroundSwitcher, Measure, Snapshot (and more are coming).
We have also implemented a JSX based templating system for advanced data viewing.
In case you wondered, we are already using the MapStore 2 framework in some of the applications we are developing for our clients and 2 of them are already being used in production, so yes, MapStore 2 is not a toy, it can be used in production too!
As an example we have recently migrated an application based on ExtJS and OpenLayers 2 (see pictures below) to show geological and other kind of data with a powerful timeslider functionality and support for many different coordinate systems (also polar ones). The application is a clear example of the level of customization MapStore 2 allows with relative ease (the customer explicitly asked to mimic the L&F of the old application in many places).
For another client, we are building a configurable system to query (spatial) data published as complex features on GeoServer (through the app-schema extension), using OGC protocols (WMS and WFS). The system leverages the already mentioned data visualization tools available in MapStore 2, in particular the QueryBuilder, the FeatureGrid and the JSX based templating system.
During this journey we have already learned a lot of stuff; we learned something good:
- the declarative programming paradigm of ReactJS is good, and together with the one-way-dataflow of Redux makes the application more predictable
- simple and dumb components are more reusable, through the usage of composition
- separating state from components makes components more reusable too
- quality (unit testing, continuous deploy and so on) needs to be there from the beginning to be successful
We also learned something bad:
- the build process can become painful very soon, and this still needs some love from us, to effectively get out of the npm dependency hell (do you remember of the maven dependency hell?)
- introducing many new technologies at the same time can be confusing, and requires time to get ingested, mainly to understand what to use and what not, and how to use it
- integrating ReactJS components to drive Non-ReactJS librarties (such as OpenLayers 3 and LeafletJS) is the point where most of the headaches will come from, so it’s essential to mantain the area of this surface at the minumum
However, the MapStore 2 journey is not finished yet, there is still some work ahead of us to have a full replacement of MapStore 1 and we are targeting end of May 2016 for a first release.
In addition, right now the look & feel is still a bit thrown together and the usability needs some love: we are working on this with a specialized partner as we want a captivating and easy to ise user interface for MapStore 2.
It is worth to point out that some of the work mentioned in this post has been performed under the umbrella of our GeoSolutions Enterprise Support Services offer.
If you want to know more about how we can help your organization, don’t hesitate and get in touch with us! Make sure to check our Enterprise Services Offer which has been used to complete the work described in this post.
The GeoSolutions Team,