Wednesday, 11 January 2017

React-ing to the need for a modern MapGuide viewer (Part 9): It's like a Christmas tree!

After figuring out how to make an accordion and getting the 0.7 release out the door, I needed to do some necessary refactoring of how we're currently modelling application state in redux to make my intended marquee feature of the next release possible.

After the refactoring, I saw some noticeable sluggishness in the viewer across all templates. This is where my choice to build this viewer on top of React has been validated (once again for the umpteenth time!), as it comes with top-notch developer tool support. So I flicked over to the React Developer Tools, activated update tracing and lo and behold ...


The viewer lit up like a Christmas tree, without any user interaction! What this was showing was that some React component was constantly updating and re-rendering itself and the sluggishness I was experiencing was in part due to that.

So it was clear that the refactoring caused some component to constantly be updating. But what?

That's where I turned to a new feature in the latest 15.4 release of React, which is by simply appending react_perf to the query string


It activates performance profiling in React itself. I then switched to the Timeline tab in the Chrome devtools and recorded 5 seconds of this "idle" activity


And we see there is activity that corresponds to this constant updating/re-rendering. If we drill down to one of these hot spots.


We finally identify the culprit:

  • It is the map viewer component
  • Something is causing the busy count to constantly go up and down. We store busy count as redux state so that the Navigator (aka. Zoom slider) component can listen on to determine whether to show/hide the "busy" animating indicator at the top of the component
The incrementBusyWorker and decrementBusyWorker functions are registered handlers for the imageloadstart and imageloadend events provided by OpenLayers MapGuide image sources. The only way these events would constantly fire from OpenLayers would be if something was constantly refreshing the map.

Which meant the possible suspect location is most likely in the component's componentWillReceiveProps lifecycle hook function which we use to interact with OpenLayers in response to component prop changes.


I stuck a breakpoint here to see if it was being hit while "idle", and it did!


While paused in this breakpoint, I evaluated the above two expressions to see what was being compared

And there was the problem! My visibility difference check was insufficient as it only did a shallow object reference comparison. The possible cause for this behaviour? My refactoring and over-zealous use of the new object spread operator in TypeScript 2.1 for shallow state cloning in my various redux reducers exposed and brought this problem into the light.

The fix was two parts. Firstly, to replace these (obviously incorrect) checks with helper functions that not only check the references (a null/undefined to set reference transition or vice versa is still a legit difference), but the actual object properties themselves.

Secondly, the layerGroupVisibility prop was a nested object prop that was being created in our component's connect function. This is actually a bad thing, which upon further reading makes perfect sense as returning new objects made in connect() will break the shallow object comparison that redux will do against a previous connect-invocation to short-circuit unnecessary component re-rendering. The fix here is to flatten this particular prop by replacing the layerGroupVisibility prop with the 4 individual array props and update all prop usages/references in the component code accordingly.

With this change in place, turning on React update tracing again shows that it no longer flashes around like a Christmas tree.


So what were the lessons learned here?
  • Component props and state, and how you structure and compare them are important when it comes to performance. The endless articles out there on the art of properly implementing shouldComponentUpdate underscores how important this fact is.
  • On the same vein, Redux has its own set of best practices for performance. This is good reference.
  • React's developer tooling is top-notch, and allows for performance issues like this one to be easily identified and debuggable.
Now to make sure this major refactoring didn't regress any other stuff.

Saturday, 7 January 2017

Announcing: mapguide-react-layout 0.7.1

Here's a quick bug fix release to address the following issues:

The base layer switcher now has a "NONE" option just like its Fusion counterpart


Thanks to some insights from the Blueprint devs, the Accordion component in the Slate and Maroon templates now initially shows the Task Pane when loaded as I had originally intended.

Finally, the scale display dropdown (shown when you have tiled maps) should now properly work when selecting a fractional scale.

Oh, and here's something I left out of the new features for 0.7. The viewer options now actually does something instead of being an empty placeholder. You can use it to toggle feature tooltips on/off should you not have a MapTip present in your toolbar.


Download

Tuesday, 3 January 2017

Announcing: mapguide-react-layout 0.7

It's a new year and the releases keep on coming!

Here's a summary of changes since the last release

Blueprint as UI foundation

I've adopted Blueprint as the UI foundation for mapguide-react-layout. This gives us a rich and cohesive toolbox of various React UI components and key components that allow us to port over the remaining Fusion templates across. Various UIs such as QuickPlot, Measure, Selection Panel, etc have got the Blueprint facelift.

 


All Fusion Templates available

Thanks to components provided by Blueprint, we now have the key ingredients to bring across the remaining Fusion templates.






Other Changes

The BasemapSwitcher widget has been ported over


An initial cut of the Geolocation widget has also been ported across


The scale display is also editable


For tiled maps, this turns into a dropdown


Also the zoom slider, it better reflects reality in terms of positioning, especially for tiled maps.


And much more is available from the release notes from the download link below.

Download

Announcing: mapguide-rest 1.0 RC5

Here's a long awaited new release of mapguide-rest.

This release is PHP 5.6 compatible (so it will work with MapGuide Open Source 3.1, that bundles PHP 5.6) and includes various fixes that can be found in the referenced change log

Download

So ... what's stopping me from putting an end to these endless 1.0 RC releases?

I need to answer (and commit to it) the age old question of: What is the best strategy for versioning REST APIs? Currently, I'm leaning towards the "put the version in the URL" approach. If you have played around with the mapguide-rest addin for Maestro, that is what the "v1" in the url represents when you connect to mapguide-rest from Maestro.


The bits I still have to figure out is once the "v1" API is set in stone, how do we evolve the API afterwards. I need a solid plan for post-v1 API evolution in place before I can slap the 1.0 final label.

Thursday, 29 December 2016

About that grand plan of 2016 ...

Not all plans go as intended. In my case, it was my grand plan for this year.

If you thought this was the mapguide-react-layout project, then I'm afraid you were mistaken. It was actually something else entirely (I did stress in that post that the project won't be MapGuide-specific). However, due to external circumstances, this plan never and probably will not materialize because I no longer see the need for it.

So since I don't see this grand plan going ahead in the near future, I might as well talk about what it was going to be. Basically, I was wanting to make a Cesium-based replacement for the Google Earth desktop application, using the stack of:

Why did I want to build this application (which strangely sounds like some chemistry/physics experiment, what's with these projects co-opting their terminology?). I wanted a replacement for Google Earth. 

Google Earth was good for only one thing: Visualizing KML files referenced against good quality satellite imagery.

It was absolutely horrible in all other aspects:
  • I can't mash KML together other geospatial data sources (SHP files, GeoJSON, etc)
  • KML is a horrible data interchange format not helped by the fact that Google Earth can't import/export to-and-from different geospatial data formats out of the box! You need a 10+ step data transformation pipeline involving OGR, QGIS and friends to get spatial data in and out of KML. OGR having 2 KML drivers adds an extra layer of confusion to the mix with one guaranteeing to trash any semblance of intelligence by outputting dumb KML geometry.
  • Creating KML files is a chore because of GE's cumbersome drawing tools
  • Modifying KML files is an even greater chore because GE lacks basic GIS-y tools like buffering, split, etc.
Going all in on QGIS wasn't desirable either because while it had the kitchen sink of GIS tools and functionality and rich support for countless geospatial data formats, it lacked the rich satellite imagery that GE provided and I've had a mixed experience with the various plugins available for QGIS that allowed me to drop-in OSM/Bing/GoogleMaps for real-world context when creating/editing features.

My hypothetical application was intended to be the sweet spot in between GE and QGIS and would've addressed most of these pain points with Cesium/OpenLayers doing most of the heavy lifting (Cesium has strong KML visualization capabilities and OpenLayers is the swiss army knife supporting many vector data sources and has rich editing capabilities) and having it all housed within an Electron host for a desktop experience.

But as I said, due to external circumstances, this idea has been mostly abandoned now. Those circumstances being mainly: I've found something that already addresses most of my pain points with Google Earth.

It's called geojson.io. Despite its deceptive name^ it solved most of my grievances with Google Earth:
  • I can bring in KML files and can easily visualize its geographic form
  • It provides serviceable tools for easily editing these geographic features. Creating new features is similarly dead simple. As these loaded features are now GeoJSON I can also easily hand-edit the GeoJSON itself if required.
  • Once done, I can export the data back out as KML
  • The provided mapbox vector/satellite layers provide a decent backdrop for geographic reference when drawing new features or editing existing ones.
So here's something that did 80-90% of what I needed from Google Earth without any of the pain and hassle of using Google Earth itself. The remaining percentages can be easily filled out with QGIS for editing beyond simple tracing of polygons and using GeoJSON or SHP as the interchange format (instead of KML) when I need to sling data between geojson.io and QGIS. The only time I needed to touch KML was when I am importing it into geojson.io or exporting it out. As a result, there was no longer such a need for this hypothetical replacement for Google Earth. I had what I wanted.

Now having said that, this wasn't ultimately all for nothing. I did get some fruits from this aborted plan. The mapguide-react-layout project is one (it uses most of the same technologies that I had originally intended to use for this Google Earth replacement). Another project (which I'll reveal in due course) was also born from the initial exploration phase of this idea.

But hey, if you think we could use an Electron/Cesium-based replacement for the Google Earth desktop application, by all means feel free to take this idea and run with it.

^ Seriously, I had known about the existence of this site for ages, but because of the name of the site I always assumed it only dealt in GeoJSON and glossed over the fact that I could import in KML and export back out as KML. This was the game-changing feature for me where one of my job workflows was having to produce KML files for certain geographic areas and the discovery of this feature made me scrap this grand plan. May I suggest a different name? Like ... GeoFiddle? That's what they call all online data playgrounds.

Friday, 23 December 2016

React-ing to the need for a modern MapGuide viewer (Part 8): I figured out how to make an accordion

Previously, I wrote about how the introduction of Blueprint as our UI toolkit gave us a nice solid set of UI components that not only replaces a whole bunch of existing (and disjoined) react components, but also provides all the necessary components to port over all of the remaining Fusion templates across.

Well that statement was half true, we got 3 of the 5 Fusion templates ported across. The remaining templates (Slate and Maroon) required an accordion-like component to dock the Legend/TaskPane/SelectionPanel in its respective sidebar.

Sadly, the Blueprint toolkit did not offer an accordion component out of the box. But based on some encouraging words, I set out to build my own accordion using the Collapse component as the foundation. A few hours later, I got a basic accordion component working, except I ran into that age old problem of how to get an element to fill 100% of its available height.

What this meant was that the "expanded" content needed either a fixed height (not acceptable) or some way where we can pre-calcuate the maximum available height for the "expanded" content when rendering the "expanded" content in the accordion. Fortunately, it turns out that there was a component that can do this legwork for us, it's called react-measure and using it was dead simple: Just wrap it around the component you need the height for and it will automagically calculate it for you.

And with that, we got a functional accordion to house our various components.


And if the look of that accordion looks familiar, you're right. Because now that we have a functional accordion, we can (and have) ported across the remaining Fusion templates. So say hello to the fully ported set of Fusion templates for mapguide-react-layout.

Slate


Maroon


Aqua


TurquoiseYellow


LimeGold



Expect a new release of mapguide-react-layout in the New Year with these Fusion templates and many other goodies in store.

Wednesday, 14 December 2016

My Oracle XE setup adventure with Docker

NOTE: This blog post was left in draft limbo for several months. Some things in this post may feel disjointed and some things are now obviously being referenced in the past tense. 

DISCLAIMER: This is not an authoritative Oracle installation guide. Neither is this post the opinions of someone with expert knowledge of Oracle. This is just a blogger's tale of getting an installation of Oracle XE up and running with Docker with the minimum of pain and unnecessary administration so he can easily have an Oracle XE instance available for testing with MapGuide/FDO and other geospatial software.

So before I start the release cycle for the first beta release of MapGuide Open Source 3.1, I'd thought I'd try to investigate (and try to knock off) some long standing issues with Oracle with the King FDO provider.

Naturally, this requires I have an installation of Oracle (XE at a minimum) lying around, which I don't. So that means I've to install it. Having had the displeasure of working with Oracle in the past and encountering its horrible, UI-from-the-last-millennium setup process and administrative UIs, I really didn't want to go down that path.

And in YEAR(GETDATE()), I should no longer have to install Oracle to a bare metal OS. I shouldn't even have to do it inside a clean test Virtual Machine. We have docker, and there's bound to be an Oracle docker image on the docker hub, that I can pull down, spin up a container and have an Oracle installation ready to go.

So that's what I set out to do. Since Windows is the first priority platform of focus for any MapGuide/FDO issues, I needed to get docker installed on Windows. Unlike Linux, Docker support on Windows is not first-class (they're working on that ^) and currently requires a virtualization layer (eg. VirtualBox) for docker on windows to interface with. Fortunately, docker provides the Docker Toolbox package which makes setting up Docker on Windows or Mac an easy one click installer affair with everything to get docker up and running on Windows or Mac
  • Docker engine
  • Docker machine
  • Docker compose
  • VirtualBox to interface with the barebones Linux VM hosting the docker engine
  • Kitematic, for downloading of docker images and spinning up of docker containers through an easy to use GUI
With docker installed, it was now a case of finding an appropriate Oracle docker image on the docker hub, and Kitematic makes this dead simple. So after typing in "oracle xe" a few images were available, I gravitated towards the one with the one with the most likes and downloads.


Clicking the Create button starts the downloading process


Once downloaded, Kitematic automatically spins up a running container for the freshly downloaded docker image. Kitematic has a Web Preview option, which I presume points to the Oracle APEX manager, which I recall from my frail memory was the web-based admin for an Oracle XE installation. I don't know what black magic Kitematic does to auto-magically know this container was web-previewable, but the fact this was available was much appreciated. So clicking the preview button opened up Chrome to what I presumed to be the URL of Oracle APEX.


Well, at this point I got an unexpected http authentication prompt. Okay, so I tried the documented login for this container with no luck. What the ...? Okay, so I cancelled the prompt and tried refreshing the URL and then it suddenly takes me to the Oracle APEX login page. Weird!

So anyways, now that I'm at the login page, I login with the given credentials and was greeted with the visual confirmation that I now had a running Oracle XE installation via docker!


Except, this is not the APEX web admin, it's actually the database home page. Turns out, if I put in this address manually in the web browser (http://192.168.99.100:32768/apex) it will take me to APEX, if I open this through the web preview option in Kitematic, it takes me to the database home page. Weird!

Still at the end of the day, I had a running Oracle XE instance. Much more pleasant than my previous Oracle installation experiences!

Now it's time to bootstrap this Oracle database with some spatial data, so in the interest of eating my own dogfood, I gave FDO Toolbox a spin to see if I can load data into the fresh Oracle XE instance. So first things first, we check the running container in Kitematic for the IP and ports of this container and the ports that are exposed.


Then I proceed to use FDO Toolbox to try and create an Oracle data store via the King Oracle provider



But then ...



No dice, I don't know what happened here. I swore recalling some time ago in the long distant past that it should've been possible to bootstrap a clean Oracle database from scratch with the King Oracle FDO provider and FDO Toolbox, but try as I might, I just couldn't get it to work this time round!

So when FDO failed, it was time to make the hot tag and bring OGR into the fold. Running the venerable ogr2ogr utility brought the SHP file data into oracle flawlessly. Except, it is excruciatingly s l o w to get anything out of this Oracle database! A basic spatial table listing from (eg. A FDO feature class listing) takes an eternity.

It was a this point I paged a resident Oracle expert and realized that one shouldn't try connecting and loading in data as the SYS/SYSTEM user as that it means we bring in EVERYTHING when doing the simplest thing like listing spatially-enabled tables from SDO_GEOM_METADATA

So I fire up SQLPlus and connect to this XE container using the documented admin login

sqlplus system/oracle@//192.168.99.100:32769/xe

I then make a MapGuide oracle user

SQL> create user mapguide identified by mapguide;

Then give it sufficient privileges

SQL> grant CREATE SESSION, ALTER SESSION, CREATE DATABASE LINK, -
>   CREATE MATERIALIZED VIEW, CREATE PROCEDURE, CREATE PUBLIC SYNONYM, -
>   CREATE ROLE, CREATE SEQUENCE, CREATE SYNONYM, CREATE TABLE, -
>   CREATE TRIGGER, CREATE TYPE, CREATE VIEW, UNLIMITED TABLESPACE -
>   to mapguide;

Then I exit SQLPlus and proceed to ogr2ogr my sample SHP file into this user's oracle schema

ogr2ogr -f OCI OCI:mapguide/mapguide@192.168.99.100:32769/xe Parcels.shp

Now when I connect with FDO Toolbox again (with emphasis on specifying my oracle user's schema, otherwise it's going to I presume, hit ALL_SDO_GEOMETRY_METADATA un-filtered, which performance-wise means we'd be back to slow square one!)



It is now connecting and performing at the performance level we are expecting.



And if we can connect to this Oracle XE database with FDO Toolbox, it means we can also connect to it with MapGuide proper, which is a case of setting up a new Feature Source, with the same connection parameters.


And from here, it's the standard MapGuide authoring process of creating layers pointing to this Feature Source, styling them up and composing them together on a map.




So what have I learned from this exercise?
  1. Docker is just plain awesome. I didn't want to touch the Oracle installation process and I also didn't want to "taint" my host environment with an Oracle installation either. Docker made spinning up a repeatable and disposable Oracle XE environment dead simple. Even docker on windows, which requires a Virtual Machine intermediate layer is still a seamless experience thanks to the Docker Toolbox suite of integrated tools.
  2. Either something regressed or FDO Toolbox will need some work to be able to load data into Oracle using the King.Oracle provider, which is strange as FDO is supposed to be an abstraction layer and FDO Toolbox is just simply working against the abstractions provided by the FDO API. If I have to code in Oracle-specific code paths and behaviour to get this functionality to work, then that is a sign of a leaky abstraction. I hope I don't have to do this!
  3. Thankfully where FDO failed in this task, OGR was able to pick up the slack. FDO and OGR have this nice duality where one's strengths covers the other's weakness.
  4. Always create a new Oracle user after install and load the spatial data into this user's schema, so you don't have to ask yourself why the default SYS/SYSTEM login/schema takes an eternity querying SDO_GEOM_METADATA tables.
  5. Be sure to set that OracleSchema connection property in your FDO connection, even if it's a non-SYSTEM user.
Many thanks to Gordon Luckett, for the various pointers on Oracle.

^ Windows 10 and Server 2016 now have native docker hosts which can run both Linux and Windows containers. No more intermediate virtualization layer required. Being able to containerize windows applications is game changer! Older versions of Windows will still have to go the virtualization route via VirtualBox for the Windows Docker client to work.