Skip to content

Commit

Permalink
#91: Add ChoroplethMapbox chart with docs
Browse files Browse the repository at this point in the history
  • Loading branch information
kMutagene committed Jul 7, 2021
1 parent 4574d6b commit ad72862
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 16 deletions.
5 changes: 4 additions & 1 deletion docs/5_2_choropleth-map.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,10 @@ let choroplethGeoJSON =
)
|> Chart.withMap(
Geo.init(
Scope=StyleParam.GeoScope.Usa
Scope=StyleParam.GeoScope.NorthAmerica,
Projection=GeoProjection.init(StyleParam.GeoProjectionType.AzimuthalEqualArea),
ShowLand=true,
LandColor = "lightgrey"
)
)
|> Chart.withSize (800.,800.)
Expand Down
118 changes: 117 additions & 1 deletion docs/6_2_choropleth-mapbox.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,121 @@ index: 3
*Summary:* This example shows how to create choropleth maps using Mapbox layers in F#.
*More coming soon<sup>TM</sup>*
Choropleth Maps display divided geographical areas or regions that are coloured, shaded or patterned in relation to
a data variable. This provides a way to visualise values over a geographical area, which can show variation or
patterns across the displayed location.
This choropleth map version uses [Mapbox Layers]({{root}}/6_0_geo-vs-mapbox.html). For the Geo variant, head over [here]({{root}}/5_2_choropleth-map.html)
ChoroplethMapbox charts need GeoJSON formatted data.
[GeoJSON](https://en.wikipedia.org/wiki/GeoJSON) is an open standard format designed for representing simple geographical features, along with their non-spatial attributes.
GeoJSON, or at least the type of GeoJSON accepted by plotly.js are `FeatureCollection`s. A feature has for example the `geometry` field, which defines e.g. the corrdinates of it (think for example the coordinates of a polygon on the map)
and the `properties` field, a key-value pair of properties of the feature.
If you want to use GeoJSON with Plotly.NET (or any plotly flavor really), you have to know the property of the feature you are mapping your data to. In the following example this is simply the `id` of a feature, but you can access any property by `property.key`.
Consider the following GeoJSON:
*)

// we are using the awesome FSharp.Data project here to perform a http request
#r "nuget: FSharp.Data"

open FSharp.Data
open Newtonsoft.Json

let geoJson =
Http.RequestString "https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json"
|> JsonConvert.DeserializeObject // the easiest way to use the GeoJSON object is deserializing the JSON string.

(**
it looks like this:
```JSON
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"GEO_ID": "0500000US01001",
"STATE": "01",
"COUNTY": "001",
"NAME": "Autauga",
"LSAD": "County",
"CENSUSAREA": 594.436
},
"geometry": {
"type": "Polygon",
"coordinates": [[[-86.496774, 32.344437], [-86.717897, 32.402814], [-86.814912, 32.340803], [-86.890581, 32.502974], [-86.917595, 32.664169], [-86.71339, 32.661732], [-86.714219, 32.705694], [-86.413116, 32.707386], [-86.411172, 32.409937], [-86.496774, 32.344437]]]
},
"id": "01001"
}, ... MANY more features.
```
It basically contains all US counties as polygons on the map. Note that the `id` property corresponds to the [**fips code**](https://en.wikipedia.org/wiki/FIPS_county_code).
To visualize some data using these counties as locations on a choropleth map, we need some exmaple data:
*)

// we use the awesome Deedle data frame library to parse and extract our location and z data
#r "nuget: Deedle"
open Deedle

let data =
Http.RequestString "https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv"
|> fun csv -> Frame.ReadCsvString(csv,true,separators=",",schema="fips=string,unemp=float")

(**
The data looks like this:
*)

data.Print()

(*** include-output ***)

(**
As the data contains the fips code and associated unemployment data, we can use the fips codes as locations and the unemployment as z data:
*)

let locations: string [] =
data
|> Frame.getCol "fips"
|> Series.values
|> Array.ofSeq

let z: int [] =
data
|> Frame.getCol "unemp"
|> Series.values
|> Array.ofSeq


(**
And finally put together the chart using GeoJSON:
*)

open Plotly.NET

let choroplethMapbox =
Chart.ChoroplethMapbox(
locations = locations,
z = z,
geoJson = geoJson,
FeatureIdKey="id"
)
|> Chart.withMapbox(
Mapbox.init(
Style=StyleParam.MapboxStyle.OpenStreetMap, // Use the free open street map base map layer
Center=(-104.6,50.45)
)
)

(*** condition: ipynb ***)
#if IPYNB
choroplethMapbox
#endif // IPYNB

(***hide***)
choroplethMapbox |> GenericChart.toChartHTML
(***include-it-raw***)
52 changes: 52 additions & 0 deletions src/Plotly.NET/Chart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3177,3 +3177,55 @@ type Chart =
?Fill = Fill ,
?Fillcolor = Fillcolor
)

/// <summary>
/// Creates a ChoroplethMapbox Chart.
///
/// Choropleth Maps display divided geographical areas or regions that are coloured, shaded or patterned in relation to
/// a data variable. This provides a way to visualise values over a geographical area, which can show variation or
/// patterns across the displayed location.
///
/// GeoJSON features to be filled are set in `geojson` The data that describes the choropleth value-to-color mapping is set in `locations` and `z`.
/// </summary>
/// <param name="locations">Sets which features found in "geojson" to plot using their feature `id` field.</param>
/// <param name="z">Sets the color values.</param>
/// <param name="geoJson">Sets the GeoJSON data associated with this trace. It can be set as a valid GeoJSON object or as a URL string. Note that we only accept GeoJSONs of type "FeatureCollection" or "Feature" with geometries of type "Polygon" or "MultiPolygon".</param>
/// <param name="FeatureIdKey">Sets the key in GeoJSON features which is used as id to match the items included in the `locations` array. Support nested property, for example "properties.name".</param>
/// <param name="Text">Sets the text elements associated with each location.</param>
/// <param name="Below">Determines if the choropleth polygons will be inserted before the layer with the specified ID. By default, choroplethmapbox traces are placed above the water layers. If set to '', the layer will be inserted above every existing layer.</param>
/// <param name="Colorscale">Sets the colorscale.</param>
/// <param name="Colorbar">Sets the Colorbar object asociated with this trace</param>
/// <param name="ZAuto">Determines whether or not the color domain is computed with respect to the input data (here in `z`) or the bounds set in `zmin` and `zmax` Defaults to `false` when `zmin` and `zmax` are set by the user.</param>
/// <param name="ZMin">Sets the lower bound of the color domain. Value should have the same units as in `z` and if set, `zmax` must be set as well.</param>
/// <param name="ZMid">Sets the mid-point of the color domain by scaling `zmin` and/or `zmax` to be equidistant to this point. Value should have the same units as in `z`. Has no effect when `zauto` is `false`.</param>
/// <param name="ZMax">Sets the upper bound of the color domain. Value should have the same units as in `z` and if set, `zmin` must be set as well.</param>
static member ChoroplethMapbox(locations,z,geoJson,
[<Optional;DefaultParameterValue(null)>] ?FeatureIdKey,
[<Optional;DefaultParameterValue(null)>] ?Text,
[<Optional;DefaultParameterValue(null)>] ?Below,
[<Optional;DefaultParameterValue(null)>] ?Colorscale,
[<Optional;DefaultParameterValue(null)>] ?Colorbar,
[<Optional;DefaultParameterValue(null)>] ?ZAuto,
[<Optional;DefaultParameterValue(null)>] ?ZMin,
[<Optional;DefaultParameterValue(null)>] ?ZMid,
[<Optional;DefaultParameterValue(null)>] ?ZMax
) =

Trace.initChoroplethMapbox (
TraceStyle.ChoroplethMapbox (
Z = z,
Locations = locations,
GeoJson = geoJson,
?FeatureIdKey = FeatureIdKey,
?Text = Text,
?Below = Below,
?Colorscale = Colorscale,
?Colorbar = Colorbar,
?ZAuto = ZAuto,
?ZMin = ZMin,
?ZMid = ZMid,
?ZMax = ZMax
)
)
|> GenericChart.ofTraceObject

15 changes: 12 additions & 3 deletions src/Plotly.NET/Playground.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,7 @@ let geoJson =

let data =
let dataString = Http.RequestString "https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv"
let byteArray = Encoding.UTF8.GetBytes(dataString)
use stream = new MemoryStream(byteArray)
Frame.ReadCsv(stream,true,separators=",",schema="fips=string,unemp=float")
Frame.ReadCsvString(dataString,true,separators=",",schema="fips=string,unemp=float")

let locations: string [] =
data
Expand All @@ -147,6 +145,17 @@ let z: int [] =
|> Series.values
|> Array.ofSeq

Chart.ChoroplethMapbox(
locations = locations,
z = z,
geoJson = geoJson,
FeatureIdKey="id"
)
|> Chart.withMapbox(
Mapbox.init(Style=StyleParam.MapboxStyle.OpenStreetMap)
)
|> Chart.Show

Chart.ChoroplethMap(
locations = locations,
z = z,
Expand Down
57 changes: 46 additions & 11 deletions src/Plotly.NET/Trace.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1170,15 +1170,16 @@ module Trace =
?Locations : seq<string>,
?Z : seq<#IConvertible>,
?Text : seq<#IConvertible>,
?Locationmode ,
?Locationmode : StyleParam.LocationFormat,
?Autocolorscale : bool,
?Colorscale,
?Colorbar,
?Colorscale : StyleParam.Colorscale,
?Colorbar : Colorbar,
?Marker : Marker,
?GeoJson,
?FeatureIdKey: string,
?Zmin,
?Zmax
?FeatureIdKey : string,
?Zmin : float,
?Zmid : float,
?Zmax : float


) =
Expand All @@ -1196,6 +1197,7 @@ module Trace =
GeoJson |> DynObj.setValueOpt choropleth "geojson"
FeatureIdKey |> DynObj.setValueOpt choropleth "featureidkey"
Zmin |> DynObj.setValueOpt choropleth "zmin"
Zmid |> DynObj.setValueOpt choropleth "zmid"
Zmax |> DynObj.setValueOpt choropleth "zmax"

// out ->
Expand Down Expand Up @@ -1577,13 +1579,13 @@ module Trace =

static member ScatterMapbox
(
mode : StyleParam.Mode,
mode : StyleParam.Mode,
?Longitudes : #IConvertible seq,
?Latitudes : #IConvertible seq,
?Below: string,
?Connectgaps : bool,
?Fill : StyleParam.Fill,
?Fillcolor
?Below : string,
?Connectgaps: bool,
?Fill : StyleParam.Fill,
?Fillcolor : string
) =
(fun (trace:('T :> Trace)) ->

Expand All @@ -1595,5 +1597,38 @@ module Trace =
Fill |> DynObj.setValueOptBy trace "fill" StyleParam.Fill.convert
Fillcolor |> DynObj.setValueOpt trace "fillcolor"

trace
)

static member ChoroplethMapbox
(
?Z : seq<#IConvertible>,
?GeoJson,
?FeatureIdKey : string,
?Locations : seq<#IConvertible>,
?Text : seq<#IConvertible>,
?Below : string,
?Colorscale : StyleParam.Colorscale,
?Colorbar : Colorbar,
?ZAuto : bool,
?ZMin : float,
?ZMid : float,
?ZMax : float
) =
(fun (trace:('T :> Trace)) ->

Z |> DynObj.setValueOpt trace "z"
GeoJson |> DynObj.setValueOpt trace "geojson"
FeatureIdKey|> DynObj.setValueOpt trace "featureidkey"
Locations |> DynObj.setValueOpt trace "locations"
Text |> DynObj.setValueOpt trace "text"
Below |> DynObj.setValueOpt trace "below"
Colorscale |> DynObj.setValueOptBy trace "colorscale" StyleParam.Colorscale.convert
Colorbar |> DynObj.setValueOpt trace "colorbar"
ZAuto |> DynObj.setValueOpt trace "zauto"
ZMin |> DynObj.setValueOpt trace "zmin"
ZMid |> DynObj.setValueOpt trace "zmid"
ZMax |> DynObj.setValueOpt trace "zmax"

trace
)

0 comments on commit ad72862

Please sign in to comment.