Betting Insights
Product Overview
Heat Map
Many points are loaded as GeoJSON to MapBoxGL.ShapeSource
. A child MapBoxGL.HeatMapLayer
will render these points as a heat map.
The way that points combine as well as their styling is controlled by bet-maps.ts:betMapsHeatMapStyleExpression
.
Polygon (Versus) Map
Many polygons are loaded via the MapBox API to a MapBoxGL.VectorSource
. A separate GraphQL call returns an object that associates each feature (a single polygon) with data about the entrants.
This data is used to correctly style each feature.
The user may compare up the 3 entrants at once, or in the case of sports, fixed win for either side + draw. TODO WIP
Dependencies
MapBox
MapBox provides an API, as well as native SDKs that are accessed through React Native wrapper library.
Access Tokens
Currently, we have two MapBox access Tokens for UAT and Production environments.
The UAT and Production tokens contains all public scopes.
react-native-mapbox-gl/maps
- This library is a wrapper around the native Mapbox SDK for iOS and Android.
- API Key must be set before rendering a Map
turf.js
- Turf is used for geospatial calculations, like determining if the viewport is centered on the users' location.
geojson
- Provides typings for GeoJSON standards
Component Model
In mathematics, a pure function is a function that, for a given set of inputs, always returns the same output. It also produces no side effects.
In React, a pure functional component (PFC) is a component that for a given set of props, always returns the same UI and produces no side effects.
In order to simplify testing, each UI element should be built as PFC in the component library. Inside the component library it may be wrapped with another component in order to demonstrate that it can respond correctly to props.
const PureComponent = ({ disabled, onPress, text }) => {
return (
<TouchableOpacity onPress={onPress} disabled={disabled}>
<Text>{text}</Text>
</TouchableOpacity>
);
};
const ImpureComponent = ({ disabled }) => {
const [text, setText] = useState("Hello"); // <- For a given set of props, this component may produce 2 different UIs
return (
<TouchableOpacity
onPress={() => setText("You pressed the button!")}
disabled={disabled}
>
<Text>{text}</Text>
</TouchableOpacity>
);
};
Styling
There are two sources of styling information:
Styling for the map itself (roads, suburb names, buildings, map colour) is controlled through MapBox studio.
To link MapBox studio styles to React Native, pass a styleUrl
prop to MapBoxGL.MapView
. It will be in the format mapbox://styles/entain/xxxx
.
Styling of the Heat and Polygon map is done via MapBox Style Expressions. Expressions can be created and tested in MapBox studio. Once the expression is complete, it can be exported as JSON and copied to React Native.
More complex expressions are generated by functions, whose results is passed to MapBox.
Once the styling information is passed to MapBox, everything happens in native code. This means that panning and zooming around the map should have no React Native performance overhead.
Testing
Components and functions are tested can be tested with Jest. Since react-native-mapbox-gl
is rendered natively, it is not possible for Jest to grab a handle to individual map layers or elements rendered by Mapbox directly.
Implementation
Entrant ordering
- When the map is first loaded, the first 3 entrants (by entrant number) are shown on the legend. See 'Colour Ownership' for more information on how entrants are ordered in the legend.
- To simplify logic for sports, each 'entrant' has the following default entrant numbers. This forces the legend to always show Home, Draw, Away in that order, as their colour ownership cannot change.
- Home = 1
- Draw = 2
- Away = 3
Colour Ownership
When an entrant is selected, they are assigned ownership of a colour. This means that they if they remain selected, their colour cannot change.
If the entrant is deselected, that colour is now un-owned and can be assigned freely to the next selected entrant.
When displaying entrants in the Legend and Tipping Point Sheet, runners will always be in the following order:
- Red
- Green
- Blue
The entrant number has no bearing on how entrants are ordered. It is driven completely by colour ownership.
File Structure
- Components:
app/components/betting-insights/bet-maps/
- Jest Tests:
app/components/betting-insights/bet-maps/__tests__/
- Utils:
app/utils/bet-maps.ts