Adding Map and RSS Action Type Support to SUSI MagicMirror Module with React

SUSI being an interactive personal assistant, answers questions in a variety of formats. This includes maps, RSS, table, and pie-chart. SUSI MagicMirror Module earlier provided support for only Answer Action Type. So, if you were to ask about a location, it could not show you a map for that location. Support for a variety of formats was added to SUSI Module for MagicMirror so that users can benefit from rich responses by SUSI.AI. One problem that was faced while adding UI components is that in the MagicMirror Module structure, each module needs to supply its DOM by overriding the getDom() method. Therefore, you need to manage all the UI programmatically. Managing UI programmatically in Javascript is a cumbersome task since you need to create DOM nodes, manually apply styling to them, and add them to parent DOM object which is needed to be returned. We need to write UI for each element like below: getDom: function () { .... .... const moduleDiv = document.createElement("div"); const visualizerCanvas = document.createElement("canvas"); moduleDiv.appendChild(visualizerCanvas); const mapDiv = document.createElement("div"); loadMap(mapDiv,lat, long); moduleDiv.appendChild(mapDiv); ... ... } As you can see, manually managing the DOM is neither that easy nor a recommended practice. It can be done in a more efficient way using the React Library by Facebook.  React is an open source UI library by Facebook. It works on the concept of Virtual DOM i.e. the whole DOM object gets created in the memory and only the changed components are reflected on the document. Since the SUSI MagicMirror Module is primarily written in open-source TypeScript Lang (a typed superset of JavaScript), we also need to write React in TypeScript. To add React to a Typescript Project, we need to add some dependencies. They can be added using: $ yarn add react react-dom @types/react @types/react Now, we need to change our Webpack config to build .tsx files for React. TSX like JSX can contain HTML like syntax for representing DOM object in a syntactic sugar form. This can be done by changing resolve extensions and loaders config so that awesome typescript loaded compiles that TSX files. It is needed to be modified like below resolve: { extensions: [".js", ".ts", ".tsx", ".jsx"], }, module: { loaders: [{ test: /\.tsx?$/, loaders: ["awesome-typescript-loader"], }, { test: /\.json$/, loaders: ["json-loader"], }], }, This will allow webpack to build and load both .tsx and .ts files. Now that project is setup properly, we need to add UI for Map and RSS Action Type. The UI for Map is added with the help of React-Leaflet library. React-Leaflet module is a module build on top of Leaflet Map library for loading maps in Browser. We add the React-Leaflet library using $ yarn add react-leaflet Now, we declare a MapView Component in React and render Map in it using the React-Leaflet Library. Custom styling can be applied to it. The render function for MapView React Component is defined as follows. import * as React from "react"; import {Map, Marker, Popup, TileLayer} from "react-leaflet"; interface IMapProps { latitude:…

Continue ReadingAdding Map and RSS Action Type Support to SUSI MagicMirror Module with React

Map Support for SUSI Webchat

SUSI chat client supports map tiles now for queries related to location. SUSI responds with an interactive internal map tile with the location pointed by a marker. It also provides you with a link to open street maps where you can get the whole view of the location using the zooming options provided and also gives the population count for that location. Lets visit SUSI WebChat and try it out. Query : Where is london Response : City of London is a place with a population of 7556900. Here is a map:https://www.openstreetmap.org/#map=13/51.51279067225417/-0.09184009399817228 Link to Openstreetmap: City of London (Hyperlinked with https://www.openstreetmap.org/#map=13/51.51279067225417/-0.09184009399817228) <Map Tile> Implementation: How do we know that a map tile is to be rendered? The actions in the API response tell the client what to render. The client loops through the actions array and renders the response for each action accordingly. "actions": [ { "type": "answer", "expression": "City of London is a place with a population of 7556900. Here is a map: https://www.openstreetmap.org/#map=13/51.51279067225417/-0.09184009399817228" }, { "type": "anchor", "link": "https://www.openstreetmap.org/#map=13/51.51279067225417/-0.09184009399817228", "text": "Link to Openstreetmap: City of London" }, { "type": "map", "latitude": "51.51279067225417", "longitude": "-0.09184009399817228", "zoom": "13" } ] Note: The API response has been trimmed to show only the relevant content. The first action element is of type answer so the client renders the text response, ‘City of London is a place with a population of 7556900. Here is a map: https://www.openstreetmap.org/#map=13/51.51279067225417/-0.09184009399817228’ The second action element is of type anchor with the text to display and the link to hyperlink specified by the text and link attributes, so the client renders the text `Link to Openstreetmap: City of London`, hyperlinked to "https://www.openstreetmap.org/#map=13/51.51279067225417/-0.09184009399817228". Finally, the third action element is of type map. Latitude, Longitude and zoom level information are also  specified using latitude, longitude and zoom attributes. The client renders a map using these attributes. I used ‘react-leaflet’ module to render the interactive map tiles. To integrate it into our project and set the required style for the map tiles, we need to load Leaflet’s CSS style sheet and we also need to include height and width for the map component.  <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" /> .leaflet-container { height: 150px; width: 80%; margin: 0 auto; } case 'map': { let lat = parseFloat(data.answers[0].actions[index].latitude); let lng = parseFloat(data.answers[0].actions[index].longitude); let zoom = parseFloat(data.answers[0].actions[index].zoom); let mymap = drawMap(lat,lng,zoom); listItems.push( <li className='message-list-item' key={action+index}> <section className={messageContainerClasses}> {mymap} <p className='message-time'> {message.date.toLocaleTimeString()} </p>; </section> </li> ); break; } import { divIcon } from 'leaflet'; import { Map, Marker, Popup, TileLayer } from 'react-leaflet'; // Draw a Map function drawMap(lat,lng,zoom){ let position = [lat, lng]; const icon = divIcon({ className: 'map-marker-icon', iconSize: [35, 35] }); const map = ( <Map center={position} zoom={zoom}> <TileLayer attribution='' url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'      /> <ExtendedMarker position={position} icon={icon}> <Popup> <span><strong>Hello!</strong> <br/> I am here.</span> </Popup> </ExtendedMarker> </Map> ); return map; } Here, I used a custom marker icon because the default icon provided by leaflet had an issue and was not being rendered. I used divIcon from leaflet to create a custom map marker icon. When the…

Continue ReadingMap Support for SUSI Webchat