Skip to content

Instantly share code, notes, and snippets.

@voscarmv
Last active December 22, 2023 04:25
Show Gist options
  • Save voscarmv/8e1e1329dfc56634b0b0dcb67b8b4116 to your computer and use it in GitHub Desktop.
Save voscarmv/8e1e1329dfc56634b0b0dcb67b8b4116 to your computer and use it in GitHub Desktop.
Documentation for Rapid's Map Roulette integration

Map Roulette integration for Facebook's Rapid

You can learn more about how this feature was built by reading Rapid's PR#1221

Feature description

Map Roulette is a map editor similar to Rapid. In map Roulette users can solve Challenges which are composed of Tasks to be carried out. Once a user finishes all the tasks in a given challenge, the challenge is considered to be complete. You can learn more about it from their documentation.

The Map Roulette UI works by showing users Task Clusters in a given area. The tasks in a cluster can belong to one or more different challenges. Users can click on any cluster to zoom into it and break it further into smaller sub-clusters.

Users zoom in like this until they reach a point where there are no more sub-clusters, and the map only shows individual tasks. Here's an animation of how that works:

See comments on this Gist

Alex Iannicelli raised Issue #1150 to add Map Roulette functionality into Rapid. He requested a feature that tweaks Map Roulette's UI/UX a bit.

Instead of zooming into Task Clusters, he requested we display individual tasks. He also requested we add a Challenge filter, to show only tasks belonging to a given challenge. Here's how this feature ended up looking at the end of my fellowship:

See comments on this Gist

The feature needs some polishing. In the following sections I will show you how to expand on what is already done.

Relevant code and files

Here I'll show you which files are involved in this feature, as well as To-Do's related to each.

modules/services/MapRouletteService.js

This is the file in charge of calling the Map Roulette API and storing and handling all the data related to it.

TODO

This file was born of copy-pasting the OsmoseService.js file. As such many of the if-then-else checks left over correspond to object structure checks done for Osmose that are not applicable to Map Roulette. These must be removed or re-written.

Also to be removed are all of the methods that are not used by the new Rapid feature.

And finally, some naming conventions for variables and methods may need updating to conform to Map Roulette.

modules/behaviors/SelectBehavior.js

This is where the text box gets triggered to filter tasks belonging to a specific Challenge ID on a KeyUp event.

TODO

The trigger works on KeyUp, but users might copy/paste the challenge ID using their mouse. In that case, the map does not update automatically. Add a trigger for that.

modules/ui/maproulette_details.js

modules/ui/maproulette_editor.js

modules/ui/maproulette_header.js

modules/ui/view_on_maproulette.js

These are all files involved, in one way or another, in the display of Task data on the left side panel.

TODO

It might be possible to delete tasks from Rapid at one point, this is where the code to do that will live.

modules/pixi/PixiLayerMapRoulette.js

Here is where the code that plots the task points graphically on the map exists.

TODO

We probably want to beautify these to look more like Map Roulette. This is where one would do that.

Dealing with the MR API

You can find out how to query the Map Roulette API in it's documentation page.

However, as of this writing, that documentation is quite incomplete.

I personally had to try out undocumented filters on my own API calls, which you will find commented out in MapRouletteService.js

You can do this to if needed. The way to go about it is to test filters mentioned in endpoints other than the one you are using. For example, the documentation explains cid=? in /challenges but not in /tasks. However, if you use cid=? to filter /tasks it will work! Try this with different parameters if you need to.

Another way to discover undocumented filters is by using the inspect tab on Chrome or Firefox. Use the Network tab to find calls to the Map Roulette API and hunt for undocumented filters and parameters there.

This is how I found cStatus (filter by challenge status) and tbb (filter by bounding box) actually work for /taskCluster, even though they are not mentioned in the official documentation for the /taskCluster endpoint.

How to extend this feature using git

I recommend you import the already-existing branch mr-integration-Oscar into your own fork of Rapid to extend this feature.

To do this, you may try the following:

$ git remote add upstream https://github.com/facebook/Rapid
$ git checkout -b mr-integration-Oscar upstream/mr-integration-Oscar
$ git push -u origin mr-integration-Oscar

This should copy my branch mr-integration-Oscar into your forked repo. If you want you can rename it by creating a new branch once inside it:

$ git checkout mr-integration-Oscar
$ git checkout -b mr-integration-YourOwnName
$ git push -u origin mr-integration-YourOwnName

Navigating and debugging the code

For me, the most important debugging feature you can use to figure out where code is being called from in Rapid, is the step-out button.

I've already written a short guide on how to get started debugging Rapid. Please check it out for more on using step-out.

Incomplete feature: Task Cluster zoom-in

In the mr-integration-Oscar branch you can checkout commit fb2f1158528302a5b75b49f92f92f28fd318775b with git checkout fb2f1158528302a5b75b49f92f92f28fd318775b

This commit contains a replica of the UX/UI feature Map Roulette uses to progressively zoom into task clusters. You can see how it works here:

See comments on this Gist

The most relevant piece of code is probably in Task.js, which translates the polygon returned by the Map Roulette API into a Bounding Box that Rapid can use to zoom into task sub-clusters over and over:

  extent(){
    let sortX = this.task.bounding.coordinates[0].sort(
      (a, b) => {
        return b[0] - a[0];
      }
    );
    const minX = sortX[0][0];
    const maxX = sortX[sortX.length - 1][0];
    let sortY = this.task.bounding.coordinates[0].sort(
      (a, b) => {
        return b[1] - a[1];
      }
    );
    const minY = sortY[0][1];
    const maxY = sortY[sortY.length - 1][1];

    return new Extent([minX, minY], [maxX, maxY]);
  }
}

This polygon comes from the following commented-out API call in MapRouletteService.js

// const url = `${MAPROULETTE_API}/taskCluster?cLocal=0&cStatus=${encodeURIComponent('3,4,0,-1')}&ce=true&invf=&pe=true&points=25&tbb=${encodeURIComponent(urlBboxSpecifier)}`;

This may or may not be useful to implement later on, so I will just leave this here for reference.

Thanks for reading. Feel free to email me with your questions at voscarmv@gmail.com

Cheers and happy hacking!

@voscarmv
Copy link
Author

voscarmv commented Dec 22, 2023

Here is the animation showing how Map Roulette Zoom-in works

ezgif com-video-to-gif-converted (3)

Here's the animation showing how the Rapid feature works.

ezgif com-video-to-gif-converted (2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment