netjsongraph.js
netjsongraph.js copied to clipboard
[feature] Make actions bookmarkable
Clicking on a node/link to view it's details (action) should update the query parameters of the page's URL with the object's ID (or some other identifiable information).
When visiting this URL, the map should automatically focus the said node/link. It shall also open the node's/links's details if they are available.
It shall also work with geo-maps which works with co-ordinates.
We shall also add the user's actions to the browser's history using history.push.
We shall make it extendable so it possible to add more data in the query params which can trigger some operation.
Approach:
- We have 3 prefixes:
geo map,indoor map, andnetwork topology. And two things to do:
1. Add fragments to the URL
If not already present, fragments (nodeId) can be added from the onClickElement handler from config. https://github.com/openwisp/netjsongraph.js/blob/dcb23b325cf8a6b09fb8a0e27c65329d9c8fb13f/src/js/netjsongraph.config.js#L324-L345
2. Render using the fragments.
While traversing through the data inside the prepared data handler, we get the data of that particular nodeId and keep it in a separate object so that it can be retrieved afterwards without the need for full traversal. https://github.com/openwisp/netjsongraph.js/blob/dcb23b325cf8a6b09fb8a0e27c65329d9c8fb13f/src/js/netjsongraph.config.js#L298-L311
After that, we can create an event handler, AfterRender in config.js, and emit it just like onReady. https://github.com/openwisp/netjsongraph.js/blob/dcb23b325cf8a6b09fb8a0e27c65329d9c8fb13f/src/js/netjsongraph.core.js#L65-L71
That way, we can configure our custom use case based on the prefix inside the afterRender.
TLDR
We discussed the following topics in the call:
- Managing URL fragments for multiple maps on one page
- Approach for handling geographic map vs. indoor floorplan maps in openwisp-monitoring
- Deciding how to include locationId (chose encoding it in mapId)
- Logic for loading floorplans from bookmarked URLs
- Need to clear fragments when switching/closing floorplans
- Bug with popup rendering before floorplan loads
Implementation Details
How to handle URL for multiple maps on a single page?
-
Each Netjsongraph.js object has an id
-
The netjsongraph.js object will look for it's id in the URL fragment and it will only perform the operation defined for it's ID
-
The Netjsongraph.js is aware only of itself.
-
From the perspective of netjsongraph.js, we can have unlimited number of maps on the page. And each map will add it's own fragment to the URL. Example:
#id=map1&nodeId=2&zoom=4;id=map2&nodeId=4&zoom=4;id=map3&nodeId=6&zoom=4;id=map6&nodeId=7&zoom=4 -
From the perspective of openwisp-monitoring, we only have two maps on the page. Example:
#id=owGeoMap&nodeId=<locationId>&zoom=4;id=<locationId:floorNumber>&nodeId=4&zoom=2;Notes: The order of the fragment does not matter, the URL could beid=<locationId:floorNumber>&nodeId=4&zoom=2;#id=owGeoMap&nodeId=<locationId>&zoom=4;
In openwisp-monitoring, we require the locationId to fetch the indoor coordinates:
-
We thought of two options
-
Add JS logic in openwisp-monitoring which read the locationId from the fragment of the
owGeoMapmap. -
Include the locationId in the mapId of indoor map, i.e. the mapId would be
<locationId:floorNumber>.
-
-
@pandafy and @dee077 concluded that option 2 is better as it makes the netjsongraph object self-reliant.
-
When the user opens a bookmarked URL, initially only the geographic map will render. We will add JS logic in floorplan.js which will do the following tasks:
- This function is executed after
$(document).ready() - Any fragment whose
idis notowGeoMapwill be considered as fragment for the floorplan overlay - We can generate the indoor-coordinates URL from the info present in the
id(it contains both locationId and floor number) using thegetIndoorCoordinatesUrlfunction. Then using theopenFloorplanfunction, we can open the desired floorplan overlay.openFloorplanfunction only requires the URL to open the correct floor. - After this, the
NetjsonGraphobject will take over to open popup for the bookmarked node.
- This function is executed after
-
Avoiding bugs: We need to clear the fragment for floorplan node in the following scenarios:
- When the user changes the floor
- When the user closes the floorplan overlay
Do so will ensure that we don't have fragments for two different floors.
When opening the floorplan overlay using the bookmarkable URL, the pop-up appears before floorplan and nodes are rendered
Bug
-
For optimization reasons, we avoid to iterate over the data again to find the co-ordinates of the bookmarked node (we only have nodeId).
-
Instead, we store the co-ordinates of the bookmarked node in an internal attribute of the
netJSONGraphobject when the library iterates over the data inprepareData. Thus, avoiding the need to iterate over all the data again. -
In the
floorplan.js, we convert co-ordinates of the node fromCRS.SimpletoEPSG:3857. In the current implementation, we are only updating theecharts.series[0].data. -
Thus, the co-ordinates saved in
prepareDatagets outdated and the pop-up renders at wrong place.
Solution
-
Conversion of co-ordinates in floorplan.js is a special case and would not be required after we fix the real issue in netjsongraph.js
-
As a temporary fix, we convert the following logic in to scoped function and use that function to convert the co-ordinates of bookmarked node in
floorplan.js
mapOptions.series[0].data.forEach((data) => {
const node = data.node;
const px = Number(node.coordinates.lng);
const py = -Number(node.coordinates.lat);
const nodeProjected = L.point(topLeft.x + px, topLeft.y + py);
// This requrires an map instance to unproject coordinates so it cann't be done in prepareData
const nodeLatLng = map.unproject(nodeProjected, zoom);
node.properties.location = nodeLatLng;
data.value = [nodeLatLng.lng, nodeLatLng.lat];
});
- The click operation should be performed after
onReady.
PS: @dee077 I changed the id for openwisp-monitoring geographic map from dashboardGeo to owGeoMap since we will also have a dedicated page for the map.
PSS: We used arbitrary delimited (;) for separating data for different maps. We should double check and use only url safe character.
Thoughts for later
- In openwisp-monitoring, when we close the floorplan overlay, ensure all the netjsongraph.js object related to floors of the location are deleted. The goal is to ensure that the zombie objects are not listening for any events.