Showing posts with label OpenLayers. Show all posts
Showing posts with label OpenLayers. Show all posts

Thursday, 3 March 2022

Announcing: vscode-map-preview 0.5.9

This update tidies up the feature popup display so that overly long name and values are now ellipsized


The original un-ellipsized content is available as a mouseover tooltip


Although I couldn't verify it, the name and content values are now also sanitized with DOMPurify to guard against any potential XSS shenanigans.

This update also includes an updated OpenLayers (6.13.0)

Monday, 18 January 2021

Announcing: vscode-map-preview 0.5.8

This minor update fixes display of spatial data that spans the international date line by turning off the flag that causes OpenLayers to auto-wrap such features

Thursday, 31 December 2020

Announcing: vscode-map-preview 0.5.7

This final release of anything from me for 2020 fixes our KML content scrubbing code to no longer trash KML icon styles. OpenLayers didn't support KML icons properly when this extension was first created which necessitated scrubbing said content out when previewing KML files so that at least something will render in the preview instead of nothing.

That is no longer the case, so now KML icon styles are preserved when previewing. Case in point below, we now get cutlery icons instead of the standard pin marker.


One small caveat needs to be observed: Due to content security restrictions on the HTML generated by this extension, the KML icon URLs must be https otherwise nothing will render.

This release also updates OpenLayers to 6.5.0 and ol-layerswitcher to 3.8.3

Tuesday, 20 October 2020

Announcing: vscode-map-preview 0.5.6

This release updates OpenLayers to the latest 6.4.3, ol-layerswitcher to the latest 3.7.0 and adds support for opacity properties of the mapbox simple style spec for GeoJSON features.



Sunday, 24 May 2020

Announcing: vscode-map-preview 0.5.2

This release future-proofs the extension by using the recommended webview APIs and adding a CSP to the generated preview HTML used to display the map.

In the grand scheme, everything should be working as before.

Special thanks to Matt Bierner of the VSCode team for the heads up on this issue.

Saturday, 1 February 2020

Announcing: vscode-map-preview 0.5.1

This release restricts the display of the new preview UI buttons to only files whose extension we know to be preview-able


The preview buttons now only display for the following file extensions:

  • .geosjon
  • .csv
  • .kml
  • .gml
  • .gpx
  • .igc

Although preview-able content may exist within .txt, .xml and .json files, these formats are too generic to reliably determine ahead of time if this is actually the case (before the preview command is run).

The preview commands can still be run the "old fashioned" way for all other cases.

Wednesday, 29 January 2020

Announcing: vscode-map-preview 0.5.0

Having figured out the technique for seamlessly loading CSV data in mapguide-react-layout, it was clear that there was another OpenLayers-based tool of mine that could benefit as well, hence a new release of the VSCode Map Preview extension.

Here's what's new in this release.

CSV Support

Obviously!


Rather than bombard you with excessive UI prompts for what column is the X coordinate and what column is the Y coordinate, we just check for a list of common column pairs:
  • X/Y
  • Lon/Lat
  • Lng/Lat
  • Long/Lat
  • Longitude/Latitude
  • Easting/Northing
If you want to add more column pairs to search for, there is a new configuration setting to define these pairs.


Preview buttons

Having learned about VSCode menu contribution points, we now finally have buttons in the editor UI to invoke the preview commands!



Custom Projection Support

The extension now supports custom projections allowing you to preview data from projections other than 4326 or 3857. A new configuration property is available to let you define additional custom projections:


To find/register your custom projection, visit epsg.io and get the proj.4 or proj4js definition for your particular EPSG code and add a new entry with this EPSG code and definition as per the above fragment.

Because of this support, the Map Preview (with projection) command now presents a list of known projections instead of requiring you to enter the EPSG code manually


Other Changes

Vertex styling for line/polygon features can now be toggled on/off through a new configuration property.


OpenLayers has also been updated to the latest release (6.1.1) and the extension fixes some corner cases when previewing certain types of KML and GeoJSON content.

Friday, 15 March 2019

Announcing: vscode-map-preview 0.4.6

A kind user alerted me to the fact that my Map Preview extension for Visual Studio Code was using APIs that have long been deprecated and could be removed any moment in a future release, breaking this extension in the process.

Although I haven't touched this code in a while (not intentional btw. One person can only tend to so many different open source projects), getting back into VSCode extension development was a very comfortable affair:

  • Update a few packages here and there. 
  • Update TypeScript. 
  • Read up on the new Webview API, which is what our extension should be using now.
  • Replace usages of vscode.previewHtml with the new Webview API.
  • Verify the extension still works
  • Get re-acquainted with how to publish VSCode extensions
And ... voila! A new version of the Map Preview extension is available for your VSCode installation to auto-update to.


In terms of changes, beyond some updates to OpenLayers and a small bug fix or two, there isn't really much to write home about, it should still be the same extension as before. This release was driven primarily by the need to move away from their old vscode.previewHtml API over to their new Webview API.

Once things have settled down on my other projects, I'd like to give this project some more attention. There's still some cool ideas I want to explore with this extension. I don't know if I still want to go down the Cesium route as previously hinted as there have been many more data viz players that have arrived on the scene since then and I also sort of do not want to detract from the original goal and purpose of this extension: An easy way to preview on a Map textual content that you are already editing or viewing in VSCode.

Monday, 28 August 2017

React-ing to the need for a modern MapGuide viewer (Part 19): Highlighting selected features

When I was implementing the Selection Panel for mapguide-react-layout, this super-ancient Fusion ticket was on the back of my mind as being able to highlight selected features is a very useful feature to have.

Whereas I didn't really have an idea how this would be done in Fusion, for mapguide-react-layout I had an idea how this would be done.

The trick is to use the v2.6 QUERYMAPFEATURES operation with the following parameters:

  • REQUESTDATA=2 (asking for inline selection image only)
  • LAYERATTRIBUTEFILTER=0 (so that selection is not constrained by layer selectability and our current view)
  • FEATUREFILTER=(the selection XML sub-fragment that represents the selected feature)
  • PERSIST=0 (the most important parameter. We want this operation to not alter the selection set)
Once the QUERYMAPFEATURES operation is sent and we get a response, the key is to be able to line up the inline selection image (data URI) with the current map. To handle this part, we use an OL static image source to store the inline selection image.

When all the pieces are brought together, we finally have the ability to highlight selected features.


This will be in the next release of mapguide-react-layout.

Monday, 20 February 2017

React-ing to the need for a modern MapGuide viewer (Part 12): A positive cascading effect

The move to Jest for our testing/coverage needs has opened up some opportunities that were previously roadblocks.

Mainly, we can finally upgrade to Webpack 2. Previously, we were roadblocked because the karma runner just wouldn't work with webpack 2 configurations. Also unlike earlier attempts with Webpack 2 beta releases, this upgrade to Webpack 2 was less painful and more importantly, the bundle size remained the same.

Also OpenLayers recently released 4.0.0, which also includes experimental ES2015 modules, the ES2015 module facilitates a "pay for only what you use" model which is great for us as we don't necessarily want to use the kitchen sink, only the parts of the library we actually use. It turns out based on their webpack example that it requires Webpack 2 to work as Webpack 1 will include said modules verbatim causing most browsers to blow up on the various ES2015 language constructs (like imports).

Well, how convenient that we just upgraded to Webpack 2! Switching over to the new ol package and its ES2015 modules, and making the required fixes in our codebase to use this new package, and checking the final production bundle size shows promise.



That is 150kb smaller than our current production bundle! Once other libraries we're using adopt ES2015 modules, we can expect even more weight loss.

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.

Monday, 17 October 2016

Announcing: mapguide-react-layout 0.6

Previously, I mentioned a series of blocking issues (in the process of porting over the Aqua Fusion template) that I needed to clear before I can safely put out a new release of mapguide-react-layout. Well, those roadblocks have been cleared, so here's a new release.

Here's a summary of the cool stuff since the first official release.

Aqua Template

The first of 5 fusion templates have been ported across.


Here's how the road blocks were cleared

1. Task Pane / Selection Panel / Legend does not play nice with Modal Dialogs

It turns out that all we really needed to do was have each modal dialog containee component support the notion of a maximum height and overflow:scroll its inner content if that height is exceeded. As these modal dialogs are of a known height, we know what max height that will have to be applied to each top-level component that is to be housed inside a modal dialog

2. We sort of need InvokeScript support as that is what the original templates use to show the Task Pane / Selection Panel / Legend / etc. 

I've decided to nip this one in the bud and just hard code into the template 3 toggling buttons that you can see on the top-right hand corner of the screenshot. If you close any of the affected modals, you can hit the respective button to bring it back.



3. Flyout menus don't play nice either

This one looked to be hard, but thankfully the react-flyout component I've chosen allowed for my workaround to (ahem) work with minimal amount of legwork.

The key was to introduce a new top-level FlyoutRegion component which all flyout menus render into (regardless of what toolbar they come from). As this FlyoutRegion component is top-level, the flyout menus rendered inside do not have a direct parent-child relationship with the toggling button (from whatever toolbar it may originate from), therefore it is not subject to being caught up in any toolbar CSS overflow shenanigans.

The only catch with this is that all the flyout menus will always render to the top left corner (I'm guessing this wasn't a usage scenario envisioned by the react-flyout author). So as a minor hack I've introduced new redux actions to dispatch the opening/closing of flyout menus, which the FlyoutRegion listens on, and the flyout menu buttons dispatch such actions when clicked, also flowing back DOM element dimensions (of the toggling button), so the listening FlyoutRegion component knows exactly where to position the flyout menu to be rendered.

This isn't perfect, as this animation shows


But it's a small (and acceptable) price to pay for what is now mostly functional flyout menus.

Application Definition (aka. Flexible Layout) support

As porting across the Aqua template would imply, the viewer now accepts Application Definition resource ids in addition to Web Layout resource ids. Please note that currently only certain templates can accept Web Layouts and others only accept Application Definitions. You can find out which template supports what in this document.

This support is not 100% yet, and so you will invariably find error placeholders for un-implemented widgets strewn across various toolbars and menus like so.


If you hover your mouse over these items, you can see what the viewer currently doesn't support or map across yet.


So when you encounter such buttons, the only practical thing to do at the moment is to remove such buttons from your Application Definition. The error placeholder mechanism is a nice and easy way to identify such unsupported elements.

If your Application Definition has external base layers defined, this release will recognize the following base layer types:

  • OpenStreetMap: Mapnik/CycleMap/TransportMap
  • Stamen: Toner/Terrain/Watercolor

Better Startup

The initial release of mapguide-react-layout wasn't really graceful about any errors that occur during the process of initialization. All you had was a white screen of nothingness to tell you that something went wrong. So naturally in the interest of providing a good out-of-the-box experience, this was something that needed to be rectified.

If there's a problem on startup, we'll show it to you now. For example, the MapGuide Server isn't running or reachable.


Another common silent error is loading a map with an EPSG code not known to proj4js (ie. Anything not EPSG:4326 or EPSG:3857). Now for such EPSG codes that aren't known to proj4js on startup, we'll automatically fetch the appropriate proj4js definition from https://epsg.io, register it with proj4js and continue on our merry way.

More OpenLayers goodies baked in

The base map viewer component now has these extra OL controls baked in.

An overview map



A rotation reset button that's shown when you rotate the map. You can rotate the map by holding ALT+SHIFT and dragging the map to rotate it.


And lots more!

All is covered in the associated release notes for this release.

Download

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, 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

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.

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.