hvega 0.6.0.0 → 0.7.0.0
raw patch · 36 files changed
+1909/−223 lines, 36 filesdep +http-conduitdep +tagsoupdep ~aesondep ~basedep ~textnew-component:exe:getbetelgeusePVP ok
version bump matches the API change (PVP)
Dependencies added: http-conduit, tagsoup
Dependency ranges changed: aeson, base, text
API changes (from Hackage documentation)
- Graphics.Vega.VegaLite: ChTooltip :: Channel
+ Graphics.Vega.VegaLite: AlignLineBottom :: VAlign
+ Graphics.Vega.VegaLite: AlignLineTop :: VAlign
+ Graphics.Vega.VegaLite: AxLabelLineHeight :: Double -> AxisProperty
+ Graphics.Vega.VegaLite: AxX :: AxisChoice
+ Graphics.Vega.VegaLite: AxXY :: AxisChoice
+ Graphics.Vega.VegaLite: AxY :: AxisChoice
+ Graphics.Vega.VegaLite: BMColor :: BlendMode
+ Graphics.Vega.VegaLite: BMColorBurn :: BlendMode
+ Graphics.Vega.VegaLite: BMColorDodge :: BlendMode
+ Graphics.Vega.VegaLite: BMDarken :: BlendMode
+ Graphics.Vega.VegaLite: BMDifference :: BlendMode
+ Graphics.Vega.VegaLite: BMExclusion :: BlendMode
+ Graphics.Vega.VegaLite: BMHardLight :: BlendMode
+ Graphics.Vega.VegaLite: BMHue :: BlendMode
+ Graphics.Vega.VegaLite: BMLighten :: BlendMode
+ Graphics.Vega.VegaLite: BMLuminosity :: BlendMode
+ Graphics.Vega.VegaLite: BMMultiply :: BlendMode
+ Graphics.Vega.VegaLite: BMNormal :: BlendMode
+ Graphics.Vega.VegaLite: BMOverlay :: BlendMode
+ Graphics.Vega.VegaLite: BMSaturation :: BlendMode
+ Graphics.Vega.VegaLite: BMScreen :: BlendMode
+ Graphics.Vega.VegaLite: BMSoftLight :: BlendMode
+ Graphics.Vega.VegaLite: DSelectionChannel :: SelectionLabel -> Channel -> ScaleDomain
+ Graphics.Vega.VegaLite: DSelectionField :: SelectionLabel -> FieldName -> ScaleDomain
+ Graphics.Vega.VegaLite: LabelLineHeight :: Double -> AxisConfig
+ Graphics.Vega.VegaLite: LineBreakStyle :: Text -> ConfigurationProperty
+ Graphics.Vega.VegaLite: MBlend :: BlendMode -> MarkProperty
+ Graphics.Vega.VegaLite: NumberRangeLL :: Double -> FilterRange
+ Graphics.Vega.VegaLite: NumberRangeUL :: Double -> FilterRange
+ Graphics.Vega.VegaLite: Product :: Operation
+ Graphics.Vega.VegaLite: TStrings :: [Text] -> TextChannel
+ Graphics.Vega.VegaLite: data AxisChoice
+ Graphics.Vega.VegaLite: data BlendMode
- Graphics.Vega.VegaLite: AxisBand :: [AxisConfig] -> ConfigurationProperty
+ Graphics.Vega.VegaLite: AxisBand :: AxisChoice -> [AxisConfig] -> ConfigurationProperty
- Graphics.Vega.VegaLite: AxisDiscrete :: [AxisConfig] -> ConfigurationProperty
+ Graphics.Vega.VegaLite: AxisDiscrete :: AxisChoice -> [AxisConfig] -> ConfigurationProperty
- Graphics.Vega.VegaLite: AxisPoint :: [AxisConfig] -> ConfigurationProperty
+ Graphics.Vega.VegaLite: AxisPoint :: AxisChoice -> [AxisConfig] -> ConfigurationProperty
- Graphics.Vega.VegaLite: AxisQuantitative :: [AxisConfig] -> ConfigurationProperty
+ Graphics.Vega.VegaLite: AxisQuantitative :: AxisChoice -> [AxisConfig] -> ConfigurationProperty
- Graphics.Vega.VegaLite: AxisTemporal :: [AxisConfig] -> ConfigurationProperty
+ Graphics.Vega.VegaLite: AxisTemporal :: AxisChoice -> [AxisConfig] -> ConfigurationProperty
- Graphics.Vega.VegaLite: DSelection :: Text -> ScaleDomain
+ Graphics.Vega.VegaLite: DSelection :: SelectionLabel -> ScaleDomain
Files
- CHANGELOG.md +48/−0
- README.md +136/−11
- default.nix +1/−1
- hvega.cabal +21/−1
- hvega.nix +3/−1
- images/example.png binary
- shell.nix +1/−1
- src/Graphics/Vega/Tutorials/VegaLite.hs +2/−1
- src/Graphics/Vega/VegaLite.hs +220/−29
- src/Graphics/Vega/VegaLite/Configuration.hs +75/−20
- src/Graphics/Vega/VegaLite/Core.hs +36/−8
- src/Graphics/Vega/VegaLite/Foundation.hs +21/−5
- src/Graphics/Vega/VegaLite/Mark.hs +80/−0
- src/Graphics/Vega/VegaLite/Scale.hs +27/−2
- src/Graphics/Vega/VegaLite/Time.hs +1/−6
- src/Graphics/Vega/VegaLite/Transform.hs +7/−0
- tests/AxisTests.hs +62/−11
- tests/ConfigTests.hs +20/−4
- tests/FilterTests.hs +85/−0
- tests/Gallery/Facet.hs +50/−0
- tests/Gallery/Label.hs +13/−6
- tests/Gallery/Line.hs +82/−77
- tests/Gallery/Multi.hs +38/−0
- tests/MarkTests.hs +130/−0
- tests/Test.hs +4/−0
- tests/TransformTests.hs +25/−0
- tests/specs/axis/axisstylexastyle.vl +34/−0
- tests/specs/axis/multiline.vl +37/−0
- tests/specs/axis/singleline.vl +28/−0
- tests/specs/config/breaklinecfg.vl +40/−0
- tests/specs/gallery/facet/trellisareaseattle.vl +69/−0
- tests/specs/gallery/label/baselines.vl +128/−25
- tests/specs/gallery/line/conditionalaxis.vl +76/−0
- tests/specs/gallery/multi/selectandzoom.vl +78/−0
- tools/GetBetelgeuse.hs +141/−0
- tools/PlayTutorial.hs +90/−14
CHANGELOG.md view
@@ -1,6 +1,54 @@ For the latest version of this document, please see [https://github.com/DougBurke/hvega/blob/master/hvega/CHANGELOG.md](https://github.com/DougBurke/hvega/blob/master/hvega/CHANGELOG.md). +## 0.7.0.0++The Vega-Lite tests are now validated against version 4.7 of the+Vega-Lite schema.++### New functionality++The `BlendMode` type has been added for controlling how marks blend+with their background. This is used with the new `MBlend` constructor+for marks.++### Breaking Change++The axis style options for specific data- or mark- types (`AxisBand`,+`AxisDiscrete`, `AxisPoint`, `AxisQuantitative`, and `AxisTemporal`)+have been changed to accept an additional argument (the new+`AxisChoice` type) which defines which axis (X, Y, or both) the+configuration should be applied to. This is to support new axis+configuration options added in Vega-Lite 4.7.0.++The `ChTooltip` `Channel` constructor has been removed as support+for this channel type was dropped in Vega-Lite 4.++### New constructors++The `ScaleDomain` type has gained `DSelectionField` and+`DSelectionChannel` constructors, which allow you to link a scale+(e.g. an axis) to a selection that is projected over multiple fields+or encodings.++The `Operation` type has gained the `Product` specifier from Vega-Lite+4.6.0.++The `TextChannel` has gained `TStrings` to support multi-line labels.++The `VAlign` type has gained `AlignLineTop` and `AlignLineBottom`+(Vega-Lite 4.6.0).++`LineBreakStyle` has been added to `ConfigurationProperty`.++The height of multi-line axis labels can now be set with the+`LabelLineHeight` and `AxLabelLineHeight` properties of the+`AxisConfig` and `AxisProperty` types (Vega-Lite 4.6.0).++Numeric filter ranges, specified with `FRange`, can now be lower- or+upper-limits - `NumberRange` and `NumberRange` respectively - added to+the `FilterRange` type.+ ## 0.6.0.0 The Vega-Lite tests are now validated against version 4.5 of the
README.md view
@@ -1,9 +1,9 @@ # hvega -[](https://vega.github.io/vega-lite/)+[](https://vega.github.io/vega-lite/) Create [Vega-Lite](https://vega.github.io/vega-lite/) visualizations in-Haskell. It targets version 4.5 of the Vega-Lite specification. Note that+Haskell. It targets version 4.7 of the Vega-Lite specification. Note that the module does not include a viewer for these visualizations (which are JSON files), but does provide several helper functions, such as [toHtmlFile](https://hackage.haskell.org/package/hvega/docs/Graphics-Vega-VegaLite.html#v:toHtmlFile),@@ -22,8 +22,17 @@ This code is released under the BSD3 license. -## Example+## Examples +### Cars++The [Vega-Lite example gallery](https://vega.github.io/vega-lite/examples/) contain+a number of visualizations of the `cars.json` dataset, which has a number of+columns to display, such as "Horsepower", "Miles_per_Gallon", and "Origin". The+following code will create a visualization that plots the efficiency of the+cars (the "mpg") as a function of its Horsepower, and color-code by the+origin of the car:+ ```Haskell let cars = dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] @@ -37,10 +46,118 @@ in toVegaLite [ bkg, cars, mark Circle [], enc [] ] ``` -When viewed with a Vega-Lite aware viewer, the resultant plot is+When the JSON is viewed with a Vega-Lite aware viewer, the resultant plot is  +### Betelgeuse++In late 2019 and early 2020 the+[star Betelgeuse](https://en.wikipedia.org/wiki/Betelgeuse) - a member of the+[constellation Orion](https://en.wikipedia.org/wiki/Orion_(constellation)) -+dimmed enough that you could see it. Betelgeuse is a member of the class of+Red Supergiant stars, which are massive enough that they will go supernova+at some point, and so there was+[some speculation](https://en.wikipedia.org/wiki/Betelgeuse#2019%E2%80%932020_fading)+that we could see a "naked-eye" supernova (even though the current models+suggest that Betelgeuse has about 100,000 more years to go before this happens).+This interest lead to a lot of observations added to the+[American Association of Variable Star Observers](https://www.aavso.org/)+database, which we are going to look at below. This example is rather-more+involved than the case one, since it involves data filtering and creation,+multiple plots, faceting, and interactive selection.++```Haskell+let titleStr = "Betelegeuse's magnitude measurements, collated by AAVSO"++ w = width 600+ h = height 150++ pos1Opts fld ttl = [PName fld, PmType Quantitative, PAxis [AxTitle ttl]]+ x1Opts = pos1Opts "days" "Days since January 1, 2020"+ y1Opts = pos1Opts "magnitude" "Magnitude" ++ [PSort [Descending], yRange]+ yRange = PScale [SDomain (DNumbers [-1, 3])]++ filtOpts = [MName "filterName", MmType Nominal]+ filtEnc = color (MLegend [ LTitle "Filter", LTitleFontSize 16, LLabelFontSize 14 ] : filtOpts)+ . shape filtOpts++ circle = mark Point [ MOpacity 0.5, MFilled False ]++ encOverview = encoding+ . position X x1Opts+ . position Y y1Opts+ . filtEnc++ selName = "brush"+ pos2Opts fld = [PName fld, PmType Quantitative, PAxis [AxNoTitle],+ PScale [SDomain (DSelectionField selName fld)]]+ x2Opts = pos2Opts "days"+ y2Opts = pos2Opts "magnitude" ++ [PSort [Descending]]++ encDetail = encoding+ . position X x2Opts+ . position Y y2Opts+ . filtEnc++ xlim = (Number (-220), Number 100)+ ylim = (Number (-0.5), Number 2.5)+ overview = asSpec [ w+ , h+ , encOverview []+ , selection+ . select selName Interval [ Encodings [ChX, ChY]+ , SInitInterval (Just xlim) (Just ylim)+ ]+ $ []+ , circle+ ]++ detailPlot = asSpec [ w+ , h+ , encDetail []+ , circle+ ]++ headerOpts = [ HLabelFontSize 16+ , HLabelAlign AlignRight+ , HLabelAnchor AEnd+ , HLabelPadding (-24)+ , HNoTitle+ , HLabelExpr "'Filter: ' + datum.label"+ ]++ details = asSpec [ columns 1+ , facetFlow [ FName "filterName"+ , FmType Nominal+ , FHeader headerOpts+ ]+ , spacing 10+ , specification detailPlot+ ]++in toVegaLite [ title titleStr [ TFontSize 18 ]+ , dataFromUrl "https://raw.githubusercontent.com/DougBurke/hvega/master/hvega/data/betelgeuse-2020-03-19.json" []+ , transform+ . filter (FExpr "datum.filterName[0] === 'V'")+ . filter (FExpr "datum.magnitude < 4")+ . calculateAs "datum.jd - 2458849.0" "days"+ $ []+ , vConcat [overview, details]+ , configure+ . configuration (Axis [ TitleFontWeight Normal, TitleFontSize 16, LabelFontSize 14 ])+ $ []+ ]+```++This can be viewed as++ - an [interactive version](https://vega.github.io/editor/#/url/vega-lite/N4KABGBEAuBOCGA7AzgMwPawLaQFxgG1wIxhJUBLAG2gFNY8oATeaAVywDpKb6A5eFloEADAF0wAXmlgA5ADVZkAL4AaYhDI86DfJBbsuWeAHNEFdk1pgAPGAAsK9SVKR4yRvvgBPD6qgAxvBUAWxUrLSeBhycAFZMYAC0YABM9gCsAByZ9gCcnCIqxGLOgeiIlCaMoC5uAB4UHvg1LlDQFlS0AGLl0ADqtBQmABbQnoiYxlSQpbXhAEa0VD2I0ADKFABekfgAjPazJDAd3b0b24y7AGwaYMrEasRe0PDVt5BssNN6o9AADshcAB6IEIADunBMFmGbHmbGQ9ACvVoq04SKwQIAIug2CYAEKfADWtCBwwAbrQTPAgcZkDpSRSqUCDNTFnQqCZaPDaIkUiI+YkRABmRK7fKxZDlSAPUqQMlIxBBMb4IguFq1YaDEbKsC7dIiQ4QSDGWCE6qQdB-eABCzeRgiTjpfwwbx-HZQP7oCirGZQHidJiMVDBBGPVpQMEUJjQYaMK4iA23I0IzoBdpS5pJo7zWDw2OZ8O1b0Wc11RgEXl8-y7BMlKB2lWCx3+FKOsRhwtGlFIpjekxNQiQMvOu0lLO1aCu92Qb06MnBaWd+7hju1bvoXuIKoFwuQJFUTBvTtGyhLQN6bT8QSRQ2tF1u8boLDehe3uaUlHn0jju8LJYrdYtndfY3zvdpoE6TwumoHQZh-CcTgA85gJuY87h-Vc72QYZ4AfZpyAoM9PEvWABCEX172nCZn0QBdMNqMsd07AiiL0FhfDgtDKM8ABHNgkHA1gKApTjj3qRpzXAyC9ExHxkDAZBvQCawACkkH42BvF1Ft+UKZdC3oo4G2-LjTyoL9jVMcxLBveCoGQIJpNcJgn3gb1y1FfwhXbUDk0wHV9FoBzPz7UTmMnPCoD4gSLCEkTfKgeAGgHMgpOnABZKyLDYKwiiXLN9IgTD1SOZA3QCI9d01IZRkufVQMs01zUta1bXtZs2inTxPVnCj-VoL9gyoUMGsjaN8zAeNE2Y9dN23EyxP3Q8mLEsyLJIsjbLQo0Iqop8X2mBKjU6TlEC-EquL-ZYziAy4DjshCIOnaDeAYI6jjSpDbr2VDtsKgyEsgbDcPdLRCPM4iYKvcjnV2x8aLowHGIW0zwYs9i-Ae+zHNB-RXPc-C1qiOSKJTWg0woDMoBzPMVEM8NuL0aLVli9p4qx8SUuOJ7GEQMIqH+ldAeMi7dyJvRjDMbLcveoGcfNFzjAJ1xxagSXrJym97KWcn00QTwaewunZclWAAqsYKztC2W4aZ-iWZeNmtu2znJJOXn+cF1ovaKgrbz3dAqA4FBLn94NlJ1UWTzRyHXs2sK701eArF0FHdyugBBRVhmWqBPwThmroAUTqP5U8gWQXp0fBZDAABqMBoi4K6C9-eBFioAAFZO5sYXl7su9v-xui4fsBtKPaoKhAczqghn1vRYBqsYMIa22oGog7F29-2ytardLhEW59LHI0ABIHM1YxPF+AFgSBRl4EhaFYU4SmgUv2hjAfyl4ESOe6AP3sHESU+tZQTxWjAWgdQAp4loOyD83JZDyXVtLawQh3CfC-iiaAyB-D7nCHQBI8wtIZwzvINYAB5BO5AR7AUyDKJ4FsAhLz+HrTwAAJdAYIwA4XknA9knIkHyRzMvRAQV5LziXgNfw8x3ADTAOURurB4BgAIREYhpDyFULAAAClvoCEEYJjGcHgPAMkkpOCYBMECAAlJwMAABNHECkc5hASCGdAYADzoEJGAVgvDoD-EMUCaAkYgn0DRE+IEbIljzHQNAfxZ1AnBPvlCGMr90SkgoH8P4xIYnwLiQkhxABJRJYJ3D+KnmAGM1gECckUYgBS6AbTBDAEIXsqiAm1IUi8M2ijUCpF0pwaUyggA)++ - as a PNG:+++ ## Documentation A tutorial is provided as part of the module: it is based, as is@@ -53,21 +170,29 @@ The [Vega-Lite Example Gallery](https://vega.github.io/vega-lite/examples/) has-been converted to an-[IHaskell notebook](https://github.com/DougBurke/hvega/blob/master/notebooks/VegaLiteGallery.ipynb)+been converted to IHaskell notebooks in the+[notebooks directory](https://github.com/DougBurke/hvega/tree/master/notebooks).+Start with the+[overview notebook](https://github.com/DougBurke/hvega/blob/master/notebooks/VegaLiteGallery.ipynb),+which describes the set up, and then the individual+sections have their own notebooks (with names of+`VegaLiteGallery-<section>.ipynb`.+ Unfortunately the plots created by VegaEmbed **do not always appear** in the notebook when viewed with either GitHub's viewer or [ipynb viewer](http://nbviewer.jupyter.org/github/DougBurke/hvega/blob/master/notebooks/VegaLiteGallery.ipynb),-but things seem much better when using Jupyter Lab (rather than+but things seem better when using Jupyter Lab (rather than notebook) to create the notebooks (since Vega is natively-supported in this environment). The notebooks have been re-created+supported in this environment). However, the native support is+for Vega-Lite version 1, and so many of the more-recent+capabilities are either ignored or cause the visualization to+fail.++The notebooks have been re-created using Jupyter Lab (thanks to Tweag I/O's [JupyterWith environment](https://www.tweag.io/posts/2019-02-28-jupyter-with.html)), which should make the plots appear on GitHub (you may need to reload the notebooks as I find they don't display on the first try).--The [notebooks directory](https://github.com/DougBurke/hvega/tree/master/notebooks)-contains a poorly-curated set of examples and experiments with hvega. ## Differences to Elm Vega
default.nix view
@@ -1,2 +1,2 @@-{ nixpkgs ? import <nixpkgs> {}, compiler ? "ghc882" }:+{ nixpkgs ? import <nixpkgs> {}, compiler ? "ghc883" }: nixpkgs.pkgs.haskell.packages.${compiler}.callPackage ./hvega.nix { }
hvega.cabal view
@@ -1,5 +1,5 @@ name: hvega-version: 0.6.0.0+version: 0.7.0.0 synopsis: Create Vega-Lite visualizations (version 4) in Haskell. description: This is based on the elm-vegalite package (<http://package.elm-lang.org/packages/gicentre/elm-vegalite/latest>)@@ -176,11 +176,13 @@ DataTests EncodingTests FillStrokeTests+ FilterTests GeoTests HyperlinkTests ImageTests InteractionTests LegendTests+ MarkTests NullTests PositionTests ProjectionTests@@ -237,6 +239,24 @@ , directory , filepath , hvega+ , text+ else+ buildable: False++ default-language: Haskell2010+ ghc-options: -Wall++ +executable getbetelgeuse+ hs-source-dirs: tools/+ main-is: GetBetelgeuse.hs++ if flag(tools)+ build-depends: aeson+ , base+ , bytestring+ , http-conduit >= 2.3 && < 2.4+ , tagsoup >= 0.14 && < 0.15 , text else buildable: False
hvega.nix view
@@ -1,13 +1,15 @@ { mkDerivation, aeson, aeson-pretty, base, bytestring, containers , filepath, stdenv, tasty, tasty-golden, text, unordered-containers+, zlib }: mkDerivation { pname = "hvega";- version = "0.6.0.0";+ version = "0.7.0.0"; src = ./.; isLibrary = true; isExecutable = true; libraryHaskellDepends = [ aeson base text unordered-containers ];+ executableHaskellDepends = [ zlib ]; testHaskellDepends = [ aeson aeson-pretty base bytestring containers filepath tasty tasty-golden text
images/example.png view
binary file changed (5854 → 95089 bytes)
shell.nix view
@@ -1,2 +1,2 @@-{ nixpkgs ? import <nixpkgs> {}, compiler ? "ghc882" }:+{ nixpkgs ? import <nixpkgs> {}, compiler ? "ghc883" }: (import ./default.nix { inherit nixpkgs compiler; }).env
src/Graphics/Vega/Tutorials/VegaLite.hs view
@@ -2365,7 +2365,8 @@ {-| -In this example I display the same data as in 'starCount', but+In this example (adapted from an example provided by Jo Wood)+I display the same data as in 'starCount', but as two layers: the first is a histogram (using the 'Bar' mark), and the second displays the count value as a label with the 'Text' mark.
src/Graphics/Vega/VegaLite.hs view
@@ -13,10 +13,8 @@ University of London. It was originally based on version @2.2.1@ but it has been updated to match later versions. This module allows users to create a Vega-Lite specification, targeting __version 4__ of the-<https://vega.github.io/schema/vega-lite/v4.json JSON schema>. The-ihaskell-hvega module provides an easy way to embed Vega-Lite-visualizations in an IHaskell notebook (using-<https://vega.github.io/vega-lite/usage/embed.html Vega-Embed>).+<https://vega.github.io/schema/vega-lite/v4.json JSON schema>.+Version 0.7 of @hvega@ supports version 4.7 of the Vega-Lite specification. Although this is based on the Elm module, there are differences, such as using type constructors rather than functions for many properties -@@ -26,7 +24,11 @@ requirement! Please see "Graphics.Vega.Tutorials.VegaLite" for an introduction-to using @hvega@ to create visualizations.+to using @hvega@ to create visualizations. The+<http://hackage.haskell.org/package/ihaskell-hvega ihaskell-hvega>+package provides an easy way to embed Vega-Lite+visualizations in an IHaskell notebook (using+<https://vega.github.io/vega-lite/usage/embed.html Vega-Embed>). == Example @@ -46,44 +48,179 @@ @ In the following example, we'll assume the latter.+The aim is to create the following visualization: -Let's say we have the following plot declaration in a module:+<<images/example.png>> +However this is missing the interactive elements of the visualization, primarily+selection and zooming in the top plot changes the axis ranges of the bottom two+plots. This interactivity requires a Vega-Lite viewer such as+<https://vega.github.io/editor/#/url/vega-lite/N4KABGBEAuBOCGA7AzgMwPawLaQFxgG1wIxhJUBLAG2gFNY8oATeaAVywDpKb6A5eFloEADAF0wAXmlgA5ADVZkAL4AaYhDI86DfJBbsuWeAHNEFdk1pgAPGAAsK9SVKR4yRvvgBPD6qgAxvBUAWxUrLSeBhycAFZMYAC0YABM9gCsAByZ9gCcnCIqxGLOgeiIlCaMoC5uAB4UHvg1LlDQFlS0AGLl0ADqtBQmABbQnoiYxlSQpbXhAEa0VD2I0ADKFABekfgAjPazJDAd3b0b24y7AGwaYMrEasRe0PDVt5BssNN6o9AADshcAB6IEIADunBMFmGbHmbGQ9ACvVoq04SKwQIAIug2CYAEKfADWtCBwwAbrQTPAgcZkDpSRSqUCDNTFnQqCZaPDaIkUiI+YkRABmRK7fKxZDlSAPUqQMlIxBBMb4IguFq1YaDEbKsC7dIiQ4QSDGWCE6qQdB-eABCzeRgiTjpfwwbx-HZQP7oCirGZQHidJiMVDBBGPVpQMEUJjQYaMK4iA23I0IzoBdpS5pJo7zWDw2OZ8O1b0Wc11RgEXl8-y7BMlKB2lWCx3+FKOsRhwtGlFIpjekxNQiQMvOu0lLO1aCu92Qb06MnBaWd+7hju1bvoXuIKoFwuQJFUTBvTtGyhLQN6bT8QSRQ2tF1u8boLDehe3uaUlHn0jju8LJYrdYtndfY3zvdpoE6TwumoHQZh-CcTgA85gJuY87h-Vc72QYZ4AfZpyAoM9PEvWABCEX172nCZn0QBdMNqMsd07AiiL0FhfDgtDKM8ABHNgkHA1gKApTjj3qRpzXAyC9ExHxkDAZBvQCawACkkH42BvF1Ft+UKZdC3oo4G2-LjTyoL9jVMcxLBveCoGQIJpNcJgn3gb1y1FfwhXbUDk0wHV9FoBzPz7UTmMnPCoD4gSLCEkTfKgeAGgHMgpOnABZKyLDYKwiiXLN9IgTD1SOZA3QCI9d01IZRkufVQMs01zUta1bXtZs2inTxPVnCj-VoL9gyoUMGsjaN8zAeNE2Y9dN23EyxP3Q8mLEsyLJIsjbLQo0Iqop8X2mBKjU6TlEC-EquL-ZYziAy4DjshCIOnaDeAYI6jjSpDbr2VDtsKgyEsgbDcPdLRCPM4iYKvcjnV2x8aLowHGIW0zwYs9i-Ae+zHNB-RXPc-C1qiOSKJTWg0woDMoBzPMVEM8NuL0aLVli9p4qx8SUuOJ7GEQMIqH+ldAeMi7dyJvRjDMbLcveoGcfNFzjAJ1xxagSXrJym97KWcn00QTwaewunZclWAAqsYKztC2W4aZ-iWZeNmtu2znJJOXn+cF1ovaKgrbz3dAqA4FBLn94NlJ1UWTzRyHXs2sK701eArF0FHdyugBBRVhmWqBPwThmroAUTqP5U8gWQXp0fBZDAABqMBoi4K6C9-eBFioAAFZO5sYXl7su9v-xui4fsBtKPaoKhAczqghn1vRYBqsYMIa22oGog7F29-2ytardLhEW59LHI0ABIHM1YxPF+AFgSBRl4EhaFYU4SmgUv2hjAfyl4ESOe6AP3sHESU+tZQTxWjAWgdQAp4loOyD83JZDyXVtLawQh3CfC-iiaAyB-D7nCHQBI8wtIZwzvINYAB5BO5AR7AUyDKJ4FsAhLz+HrTwAAJdAYIwA4XknA9knIkHyRzMvRAQV5LziXgNfw8x3ADTAOURurB4BgAIREYhpDyFULAAAClvoCEEYJjGcHgPAMkkpOCYBMECAAlJwMAABNHECkc5hASCGdAYADzoEJGAVgvDoD-EMUCaAkYgn0DRE+IEbIljzHQNAfxZ1AnBPvlCGMr90SkgoH8P4xIYnwLiQkhxABJRJYJ3D+KnmAGM1gECckUYgBS6AbTBDAEIXsqiAm1IUi8M2ijUCpF0pwaUyggA the Vega Editor>.++It is rather lengthy, as it includes+data tranformation (sub-setting the data and creating a+new column), automatic faceting (that is, creating separate plots+for unique values of a data column), interactive elements+(the ability to filter a plot by selecting a subset in+another element), and some basic configuration and styling+(primarily to change the text sizes). The+"Graphics.Vega.Tutorials.VegaLite" tutorial should be+reviewed to understand how the plot works!++It's aim is to show the recent community measurements of the+brightness of+<https://en.wikipedia.org/wiki/Betelgeuse the star Betelgeuse>,+which caused much interest in the Astronomical world at the+start of 2020 as it became much fainter than normal+(although it is massive enough to go supernova, it is not+expected to happen for quite a while yet). The data shown+is based on data collated by the+<https://www.aavso.org/ AAVSO>, and converted to JSON format,+with the primary columns of interest being \"@jd@\" (the+date of the observation, in the+<https://en.wikipedia.org/wiki/Julian_day Julian day> system),+\"@magnitude@\" (the brightness of the star, reported+as an+<https://en.wikipedia.org/wiki/Apparent_magnitude apparent magnitude>),+and \"@filterName\"@ (the filter through which the measurement was+made). For display purposes we are only going to use the+\"@Vis.@\" and \"@V@\" filters (the former is a by-eye estimate,+which is less accurate but has the advantage of having been used+for a long time, and the second is measured from a in image+taken by a <https://en.wikipedia.org/wiki/Charge-coupled_device CCD detector>,+which is more accurate and repeatable, but more costly to obtain),+and the date field is going to be converted into the number of+days since the start of 2020 (via a little bit of subtraction).+For \"historical reasons\", the magnitude system used by Astronomers+to measure how bright a system is reversed, so that larger magnitudes+mean fainter systems. For this reason, the magnitude axis is reversed+in this visualization, so that as Betelgeuse dims the values drop.+ @-\{\-\# language OverloadedStrings \#\-\}+\{\-\# LANGUAGE OverloadedStrings \#\-\} -vl1 =- let desc = "A very exciting bar chart"+betelgeuse =+ let desc = \"How has Betelgeuse's brightness varied, based on data collated by AAVSO (https:\/\/www.aavso.org\/). \" +++ \"You should also look at https:\/\/twitter.com\/betelbot and https:\/\/github.com\/hippke\/betelbot. \" +++ \"It was all the rage on social media at the start of 2020.\" - dat = 'VL.dataFromRows' ['VL.Parse' [("start", 'VL.FoDate' "%Y-%m-%d")]]- . 'VL.dataRow' [("start", 'VL.Str' "2011-03-25"), ("count", 'VL.Number' 23)]- . 'VL.dataRow' [("start", 'VL.Dtr' "2011-04-02"), ("count", 'VL.Number' 45)]- . 'VL.dataRow' [("start", 'VL.Str' "2011-04-12"), ("count", 'VL.Number' 3)]+ titleStr = \"Betelegeuse's magnitude measurements, collated by AAVSO\" - barOpts = ['VL.MOpacity' 0.4, 'VL.MColor' "teal"]+ -- height and width of individual plots (in pixels)+ w = 'VL.width' 600+ h = 'VL.height' 150 - enc = 'VL.encoding'- . 'VL.position' 'VL.X' ['VL.PName' "start", 'VL.PmType' 'VL.Temporal', 'VL.PAxis' ['VL.AxTitle' "Inception date"]]- . 'VL.position' 'VL.Y' ['VL.PName' "count", 'VL.PmType' 'VL.Quantitative']+ -- Define the properties used for the "position" channels. For this example+ -- it makes sense to define as functions since they are used several times.+ --+ pos1Opts fld ttl = ['VL.PName' fld, 'VL.PmType' 'VL.Quantitative', 'VL.PAxis' ['VL.AxTitle' ttl]]+ x1Opts = pos1Opts \"days\" \"Days since January 1, 2020\"+ y1Opts = pos1Opts \"magnitude\" \"Magnitude\" ++ ['VL.PSort' ['VL.Descending'], y1Range]+ y1Range = 'VL.PScale' ['VL.SDomain' ('VL.DNumbers' [-1, 3])] - in 'VL.toVegaLite' ['VL.description' desc, 'VL.background' "white"- , dat [], 'VL.mark' 'VL.Bar' barOpts, enc []]-@+ -- The filter name is used as a facet, but also to define the+ -- color and shape of the points.+ --+ filtOpts = ['VL.MName' \"filterName\", 'VL.MmType' 'VL.Nominal']+ filtEnc = 'VL.color' ('VL.MLegend' ['VL.LTitle' \"Filter\", 'VL.LTitleFontSize' 16, 'VL.LLabelFontSize' 14] : filtOpts)+ . 'VL.shape' filtOpts -We can inspect how the encoded JSON looks like in an GHCi session:+ -- In an attempt to make the V filter results visible, I have chosen+ -- to use open symbols. It doesn't really work out well.+ --+ circle = 'VL.mark' 'VL.Point' ['VL.MOpacity' 0.5, 'VL.MFilled' False] -@-> 'A.encode' $ 'VL.fromVL' vl1-> "{\"mark\":{\"color\":\"teal\",\"opacity\":0.4,\"type\":\"bar\"},\"data\":{\"values\":[{\"start\":\"2011-03-25\",\"count\":23},{\"start\":\"2011-04-02\",\"count\":45},{\"start\":\"2011-04-12\",\"count\":3}],\"format\":{\"parse\":{\"start\":\"date:'%Y-%m-%d'\"}}},\"$schema\":\"https:\/\/vega.github.io\/schema\/vega-lite\/v4.json\",\"encoding\":{\"x\":{\"field\":\"start\",\"type\":\"temporal\",\"axis\":{\"title\":\"Inception date\"}},\"y\":{\"field\":\"count\",\"type\":\"quantitative\"}},\"background\":\"white\",\"description\":\"A very exciting bar chart\"}"-@+ -- What is plotted in the "overview" plot?+ --+ encOverview = 'VL.encoding'+ . 'VL.position' 'VL.X' x1Opts+ . 'VL.position' 'VL.Y' y1Opts+ . filtEnc -The produced JSON can then be processed with vega-lite, which renders the following image:+ -- Select roughly the last year's observations (roughly the length of+ -- time that Betelgeuse is visible)+ --+ xlim = ('VL.Number' (-220), 'VL.Number' 100)+ ylim = ('VL.Number' (-0.5), 'VL.Number' 2.5)+ overview = 'VL.asSpec' [ w+ , h+ , encOverview []+ , 'VL.selection'+ . 'VL.select' selName 'VL.Interval' ['VL.Encodings' ['VL.ChX', 'VL.ChY']+ , 'VL.SInitInterval' (Just xlim) (Just ylim)+ ]+ $ []+ , circle+ ] -<<images/example.png>>+ -- What is plotted in the "detail" plot?+ --+ selName = \"brush\"+ pos2Opts fld = [ 'VL.PName' fld, 'VL.PmType' 'VL.Quantitative', 'VL.PAxis' ['VL.AxNoTitle']+ , 'VL.PScale' ['VL.SDomain' ('VL.DSelectionField' selName fld)] ]+ x2Opts = pos2Opts \"days\"+ y2Opts = pos2Opts \"magnitude\" ++ ['VL.PSort' ['VL.Descending']] -which can also be-<https://vega.github.io/editor/#/url/vega-lite/N4KABGBEC2CGBOBrSAuMxIGMD2Abb8qUALgKay6QA0U2ADrJgJbECeRADAHQAsNkbOqSKQARgkgBfKuCgATWMVhFQECJABuFAK6kAzkQDastekh6l8YiIBMHAIz2AtBwDMTmwFZqUHNoB21mg2rtImahgWCEFQdo4uPC42PljYATE8nmGmEJGWMZBxzhyJ9sn8foFEoeEAujKmkABmBHAxGAzwesJoedEiCmQoAOQApACaTqPQU3LDUpKy2VAAJHqYABakcCIbxMR0eigA9McapADmsFwXLBvaolxM2MfrW3Bnl7BOuCykZ64uAArPTYfzUWSQUj+HByJj+C4qcKQAAeSJyUCaTFIuDkIiiVghGIErCEIjI0DoBAoRJykFgKKYBl6AhYuB6UAAkjDSHRiM9-GBBsJFqZlup2CysTi8WhUukUoIOZAAI7aWCBFiKJjnKRLBpQcSYRAXeBpfyyqAAdw2f1pkDk+kw8CYfIFIgAgmBzvBWGBSCjmPyEWBxPAwJt+gbUv4sYjeotJEA displayed in the Vega Editor>.+ encDetail = 'VL.encoding'+ . 'VL.position' 'VL.X' x2Opts+ . 'VL.position' 'VL.Y' y2Opts+ . filtEnc + detail = 'VL.asSpec' [ w+ , h+ , encDetail []+ , circle+ ]++ -- Control the labelling of the faceted plots. Here we move the+ -- label so that it appears at the top-right corner of each plot+ -- and remove the title.+ --+ headerOpts = [ 'VL.HLabelFontSize' 16+ , 'VL.HLabelAlign' 'VL.AlignRight'+ , 'VL.HLabelAnchor' 'VL.AEnd'+ , 'VL.HLabelPadding' (-24)+ , 'VL.HNoTitle'+ , 'VL.HLabelExpr' \"'Filter: ' + datum.label\"+ ]++ -- The "detail" plot has multiple rows, one for each filter.+ --+ details = 'VL.asSpec' [ 'VL.columns' 1+ , 'VL.facetFlow' [ 'VL.FName' \"filterName\"+ , 'VL.FmType' 'VL.Nominal'+ , 'VL.FHeader' headerOpts+ ]+ , 'VL.spacing' 10+ , 'VL.specification' detail+ ]++ in 'VL.toVegaLite' [ 'VL.description' desc+ , 'VL.title' titleStr ['VL.TFontSize' 18]+ , 'VL.dataFromUrl' \"https:\/\/raw.githubusercontent.com\/DougBurke\/hvega\/master\/hvega\/data\/betelgeuse-2020-03-19.json\" []+ , 'VL.transform'+ -- concentrate on the two filters with a reasonable number of points+ . 'VL.filter' ('VL.FExpr' \"datum.filterName[0] === \'V\'\")+ -- remove some \"outliers\"+ . 'VL.filter' ('VL.FExpr' \"datum.magnitude < 4\")+ -- subtract Jan 1 2020 (start of day, hence the .0 rather than .5)+ . 'VL.calculateAs' \"datum.jd - 2458849.0\" \"days\"+ $ []+ , 'VL.vConcat' [overview, details]+ , 'VL.configure'+ -- Change axis titles from bold- to normal-weight,+ -- and increase the size of the labels+ . 'VL.configuration' ('VL.Axis' ['VL.TitleFontWeight' 'VL.Normal', 'VL.TitleFontSize' 16, 'VL.LabelFontSize' 14])+ $ []+ ]++@++The 'VL.fromVL' function will create the JSON representation of the visualization,+which can then be passed to a Vega-Lite viewer (or a routine like 'VL.toHtmlFile'+can be used to create a HTML file that will display the visualization using the+<https://github.com/vega/vega-embed Vega-Embed> Javascript library).+ Output can be achieved in a Jupyter Lab session with the @vlShow@ function, provided by @ihaskell-vega@, or 'VL.toHtmlFile' can be used to write out a page of HTML that includes pointer to JavaScript files which will display a Vega-Lite@@ -342,6 +479,7 @@ , VL.ColorGradient(..) , VL.GradientProperty(..) , VL.TextDirection(..)+ , VL.BlendMode(..) -- ** Cursors --@@ -607,6 +745,7 @@ -- $axisconfig , VL.AxisConfig(..)+ , VL.AxisChoice(..) -- ** Legend Configuration Options --@@ -668,6 +807,10 @@ -- -- $update + -- ** Version 0.7+ --+ -- $update0700+ -- ** Version 0.6 -- -- $update0600@@ -1055,6 +1198,54 @@ -- $update -- The following section describes how to update code that used -- an older version of @hvega@.++-- $update0700+-- The @0.7.0.0@ release updates @hvega@ to support version 4.7 of+-- the Vega-Lite schema.+--+-- __New functionality__+--+-- The 'VL.BlendMode' type has been added for controlling how marks blend+-- with their background. This is used with the new 'VL.MBlend' constructor+-- for marks.+--+-- __Breaking Change__+--+-- The axis style options for specific data- or mark- types+-- ('VL.AxisBand', 'VL.AxisDiscrete', 'VL.AxisPoint',+-- 'VL.AxisQuantitative', and 'VL.AxisTemporal') have been changed to+-- accept an additional argument (the new 'VL.AxisChoice' type)+-- which defines which axis (X, Y, or both) the configuration should be+-- applied to. This is to support new axis configuration options added+-- in Vega-Lite 4.7.0.+--+-- The @ChTooltip@ 'VL.Channel' constructor has been removed as support+-- for this channel type was dropped in Vega-Lite 4.+--+-- __New constructors__+--+-- The 'VL.ScaleDomain' type has gained 'VL.DSelectionField' and 'VL.DSelectionChannel'+-- constructors, which allow you to link a scale (e.g. an axis) to a selection that+-- is projected over multiple fields or encodings.+--+-- The 'VL.Operation' type has gained the 'VL.Product' specifier from Vega-Lite+-- 4.6.0.+--+-- The 'VL.TextChannel' has gained 'VL.TStrings' to support multi-line labels.+--+-- The 'VL.VAlign' type has gained 'VL.AlignLineTop' and 'VL.AlignLineBottom'+-- (Vega-Lite 4.6.0).+--+-- 'VL.LineBreakStyle' has been added to 'VL.ConfigurationProperty'.+--+-- The height of multi-line axis labels can now be set with the+-- 'VL.LabelLineHeight' and 'VL.AxLabelLineHeight' properties of the+-- 'VL.AxisConfig' and 'VL.AxisProperty' types (Vega-Lite 4.6.0).+--+-- Numeric filter ranges, specified with 'VL.FRange',+-- can now be lower- or upper-limits -+-- 'VL.NumberRangeLL' and 'VL.NumberRangeUL' respectively -+-- added to the 'VL.FilterRange' type. -- $update0600 -- The @0.6.0.0@ release updates @hvega@ to support version 4.5 of
src/Graphics/Vega/VegaLite/Configuration.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-|@@ -24,6 +25,7 @@ , ScaleConfig(..) , RangeConfig(..) , AxisConfig(..)+ , AxisChoice(..) , LegendConfig(..) , TitleConfig(..) @@ -40,6 +42,10 @@ import Data.Aeson ((.=), object) +#if !(MIN_VERSION_base(4, 12, 0))+import Data.Monoid ((<>))+#endif+ import Graphics.Vega.VegaLite.Core ( AxisProperty , axisProperty@@ -120,16 +126,24 @@ , LabelledSpec , PropertySpec )- + {-| Type of configuration property to customise. See the <https://vega.github.io/vega-lite/docs/config.html Vega-Lite documentation>-for details.+for details. There are multiple ways to configure the properties+of an axis, as discussed in the Vega-Lite+<https://vega.github.io/vega-lite/docs/axis.html#config axis configuration>+documentation. Used by 'configuration'. +In @version 0.7.0.0@, the 'AxisBand' , 'AxisDiscrete', 'AxisPoint',+'AxisQuantitative', and 'AxisTemporal' were changed to accept an+additional argument ('AxisChoice'), to define which axis the configuration+should be applied to.+ In @version 0.6.0.0@: - the @Autosize@, @Background@, @CountTitle@, @FieldTitle@, @Legend@,@@ -190,13 +204,13 @@ -- @since 0.6.0.0 | Axis [AxisConfig] -- ^ The default appearance of axes.- | AxisBand [AxisConfig]+ | AxisBand AxisChoice [AxisConfig] -- ^ The default appearance of axes with band scaling. -- -- See also 'AxisDiscrete'. | AxisBottom [AxisConfig] -- ^ The default appearance of the bottom-side axes.- | AxisDiscrete [AxisConfig]+ | AxisDiscrete AxisChoice [AxisConfig] -- ^ The default appearance of axes with point or band scales. -- -- See also 'AxisBand' and 'AxisPoint'.@@ -204,19 +218,19 @@ -- @since 0.6.0.0 | AxisLeft [AxisConfig] -- ^ The default appearance of the left-side axes.- | AxisPoint [AxisConfig]+ | AxisPoint AxisChoice [AxisConfig] -- ^ The default appearance of axes with point scales. -- -- See also 'AxisDiscrete'. -- -- @since 0.6.0.0- | AxisQuantitative [AxisConfig]+ | AxisQuantitative AxisChoice [AxisConfig] -- ^ The default appearance of quantitative axes. -- -- @since 0.6.0.0 | AxisRight [AxisConfig] -- ^ The default appearance of the right-side axes.- | AxisTemporal [AxisConfig]+ | AxisTemporal AxisChoice [AxisConfig] -- ^ The default appearance of temporal axes. -- -- @since 0.6.0.0@@ -323,6 +337,14 @@ -- @since 0.6.0.0 | LineStyle [MarkProperty] -- ^ The default appearance of line marks.+ | LineBreakStyle T.Text+ -- ^ The delimiter, such as a newline character, upon which to break text+ -- strings into multiple lines. This can be over-ridden by mark or style configuration+ -- settings.+ --+ -- Added in Vega-Lite 4.6.0.+ --+ -- @since 0.7.0.0 | MarkStyle [MarkProperty] -- ^ The default mark appearance. | MarkNamedStyles [(StyleLabel, [MarkProperty])]@@ -449,9 +471,30 @@ -- used instead. +-- | Which axis should the configuration be applied to?+--+-- Added in Vega-Lite 4.7.0.+--+-- @since 0.7.0.0+data AxisChoice+ = AxXY+ -- ^ Apply the configuration to both axes.+ --+ -- This was the default behavior prior to @0.7.0.0@.+ | AxX+ -- ^ Select the X axis.+ | AxY+ -- ^ Select the Y axis.++ toAxis :: T.Text -> [AxisConfig] -> LabelledSpec-toAxis lbl acs = lbl .= object (map axisConfigProperty acs)+toAxis lbl acs = ("axis" <> lbl) .= object (map axisConfigProperty acs) +toAxisChoice :: AxisChoice -> T.Text -> [AxisConfig] -> LabelledSpec+toAxisChoice AxXY lbl = toAxis lbl+toAxisChoice AxX lbl = toAxis ("X" <> lbl)+toAxisChoice AxY lbl = toAxis ("Y" <> lbl)+ aprops_ :: T.Text -> [AxisProperty] -> LabelledSpec aprops_ f mps = f .= object (map axisProperty mps) @@ -459,18 +502,18 @@ configProperty :: ConfigurationProperty -> LabelledSpec configProperty (AreaStyle mps) = mprops_ "area" mps configProperty (AutosizeStyle aus) = "autosize" .= object (map autosizeProperty aus)-configProperty (Axis acs) = toAxis "axis" acs-configProperty (AxisBand acs) = toAxis "axisBand" acs-configProperty (AxisBottom acs) = toAxis "axisBottom" acs-configProperty (AxisDiscrete acs) = toAxis "axisDiscrete" acs-configProperty (AxisLeft acs) = toAxis "axisLeft" acs-configProperty (AxisPoint acs) = toAxis "axisPoint" acs-configProperty (AxisQuantitative acs) = toAxis "axisQuantitative" acs-configProperty (AxisRight acs) = toAxis "axisRight" acs-configProperty (AxisTemporal acs) = toAxis "axisTemporal" acs-configProperty (AxisTop acs) = toAxis "axisTop" acs-configProperty (AxisX acs) = toAxis "axisX" acs-configProperty (AxisY acs) = toAxis "axisY" acs+configProperty (Axis acs) = toAxis "" acs+configProperty (AxisBand c acs) = toAxisChoice c "Band" acs+configProperty (AxisBottom acs) = toAxis "Bottom" acs+configProperty (AxisDiscrete c acs) = toAxisChoice c "Discrete" acs+configProperty (AxisLeft acs) = toAxis "Left" acs+configProperty (AxisPoint c acs) = toAxisChoice c "Point" acs+configProperty (AxisQuantitative c acs) = toAxisChoice c "Quantitative" acs+configProperty (AxisRight acs) = toAxis "Right" acs+configProperty (AxisTemporal c acs) = toAxisChoice c "Temporal" acs+configProperty (AxisTop acs) = toAxis "Top" acs+configProperty (AxisX acs) = toAxis "X" acs+configProperty (AxisY acs) = toAxis "Y" acs -- configProperty (AxisNamedStyles [(nme, mps)]) = "style" .= object [aprops_ nme mps] configProperty (AxisNamedStyles styles) =@@ -497,6 +540,8 @@ configProperty (LegendStyle lcs) = "legend" .= object (map legendConfigProperty lcs) configProperty (LineStyle mps) = mprops_ "line" mps +configProperty (LineBreakStyle s) = "lineBreak" .= s+ configProperty (MarkStyle mps) = mprops_ "mark" mps -- configProperty (MarkNamedStyles [(nme, mps)]) = "style" .= object [mprops_ nme mps] configProperty (MarkNamedStyles styles) =@@ -1200,6 +1245,9 @@ -- ^ The named styles - generated with 'AxisNamedStyles' - to apply to the -- axis or axes. --+ -- Added in Vega-Lite 4.7.0 (although accidentally supported in @hvega@+ -- before this release).+ -- -- @since 0.6.0.0 | BandPosition Double -- ^ The default axis band position.@@ -1311,6 +1359,12 @@ -- @since 0.4.0.0 | LabelLimit Double -- ^ The maximum width of a label, in pixels.+ | LabelLineHeight Double+ -- ^ The line height, in pixels, for multi-line label text.+ --+ -- Added in Vega-Lite 4.6.0.+ --+ -- @since 0.7.0.0 | LabelOffset Double -- ^ The pixel offset for labels, in addition to 'TickOffset'. --@@ -1459,6 +1513,7 @@ axisConfigProperty (LabelFontStyle s) = "labelFontStyle" .= s axisConfigProperty (LabelFontWeight fw) = "labelFontWeight" .= fontWeightSpec fw axisConfigProperty (LabelLimit x) = "labelLimit" .= x+axisConfigProperty (LabelLineHeight x) = "labelLineHeight" .= x axisConfigProperty (LabelOffset x) = "labelOffset" .= x axisConfigProperty (LabelOpacity x) = "labelOpacity" .= x axisConfigProperty (LabelOverlap strat) = "labelOverlap" .= overlapStrategyLabel strat
src/Graphics/Vega/VegaLite/Core.hs view
@@ -1313,6 +1313,12 @@ -- ^ The maximum width of a label, in pixels. -- -- @since 0.4.0.0+ | AxLabelLineHeight Double+ -- ^ The line height, in pixels, for multi-line label text.+ --+ -- Added in Vega-Lite 4.6.0.+ --+ -- @since 0.7.0.0 | AxLabelOffset Double -- ^ The pixel offset for labels, in addition to 'AxTickOffset'. --@@ -1541,6 +1547,7 @@ axisProperty (AxLabelFontStyle s) = "labelFontStyle" .= s axisProperty (AxLabelFontWeight fw) = "labelFontWeight" .= fontWeightSpec fw axisProperty (AxLabelLimit x) = "labelLimit" .= x+axisProperty (AxLabelLineHeight x) = "labelLineHeight" .= x axisProperty (AxLabelOffset x) = "labelOffset" .= x axisProperty (AxLabelOpacity x) = "labelOpacity" .= x axisProperty (AxLabelOverlap s) = "labelOverlap" .= overlapStrategyLabel s@@ -1751,7 +1758,7 @@ -- 'Data.Function.&'). -- -- @- -- 'filter' ('FRange' "date" ('DateRange' ['Graphics.Vega.VegaLite.DTYear' 2010] ['Graphics.Vega.VegaLite.DTYear' 2017])+ -- 'filter' ('FRange' "date" ('NumberRange' 2010 2017) -- & 'FilterOpTrans' ('MTimeUnit' 'Graphics.Vega.VegaLite.Year') -- & 'FCompose' -- )@@ -1902,6 +1909,8 @@ filterProperty (FRange field vals) = let ans = case vals of NumberRange mn mx -> map toJSON [mn, mx]+ NumberRangeLL mn -> [toJSON mn, A.Null]+ NumberRangeUL mx -> [A.Null, toJSON mx] DateRange dMin dMax -> [process dMin, process dMax] process [] = A.Null@@ -1933,14 +1942,28 @@ trFilterSpec mchan fi = object (markChannelProperty mchan <> filterProperty fi) -{-|+-- | A pair of filter range data values, used with 'FRange'. -A pair of filter range data values. The first argument is the inclusive minimum-vale to accept and the second the inclusive maximum.--} data FilterRange = NumberRange Double Double+ -- ^ Select between these two values (both limits are inclusive).+ | NumberRangeLL Double+ -- ^ A lower limit (inclusive).+ --+ -- @since 0.7.0.0+ | NumberRangeUL Double+ -- ^ An upper limit (inclusive).+ --+ -- @since 0.7.0.0 | DateRange [DateTime] [DateTime]+ -- ^ Select between these two dates (both limits are inclusive).+ --+ -- If a limit is the empty list then the filter is treated as+ -- a limit only on the other value, so+ -- @DateRange [] ['Graphics.Vega.VegaLite.DTYear' 2019]@+ -- acts as an upper-limit on the date range. One of the two+ -- limits __should__ be defined, but there is no enforcement+ -- of this. -- | Types of hyperlink channel property used for linking marks or text to URLs.@@ -2184,7 +2207,7 @@ | TTimeUnit TimeUnit -- ^ Time unit aggregation of field values when encoding with a text channel. | TString T.Text- -- ^ A literal value for encoding a text property channel+ -- ^ A literal value for encoding a text property channel. See also 'TStrings'. -- -- This can be useful for a text annotation, such as: --@@ -2196,6 +2219,10 @@ -- @ -- -- @since 0.5.0.0+ | TStrings [T.Text]+ -- ^ A multi-line value. See also 'TString'.+ --+ -- @since 0.7.0.0 textChannelProperty :: TextChannel -> [LabelledSpec] textChannelProperty (TName s) = [field_ s]@@ -2215,6 +2242,7 @@ textChannelProperty (TSelectionCondition selName ifClause elseClause) = selCond_ textChannelProperty selName ifClause elseClause textChannelProperty (TString s) = ["value" .= s]+textChannelProperty (TStrings xs) = ["value" .= xs] -- | Properties of an ordering channel used for sorting data fields.@@ -3416,7 +3444,7 @@ @ transLS = 'transform'- . 'regression' \"yraw\" \"xraw\" [ 'LsAs' \"xrg\" \"yrg\" ]+ . 'regression' \"yraw\" \"xraw\" [ 'RgAs' \"xrg\" \"yrg\" ] encRaw = 'encoding' . 'position' 'Graphics.Vega.VegaLite.X' [ 'PName' \"xraw\", 'PmType' 'Graphics.Vega.VegaLite.Quantitative' ]@@ -4130,7 +4158,7 @@ jj :: A.ToJSON a => a -> Maybe A.Value jj = Just . toJSON - res = case lfields of + res = case lfields of LuFields fs -> ( jj fs, Nothing, Nothing ) LuFieldAs fas -> ( get1 fas, get2 fas, Nothing ) LuAs s -> ( Nothing, jj s, Nothing )
src/Graphics/Vega/VegaLite/Foundation.hs view
@@ -580,18 +580,32 @@ data VAlign = AlignTop- -- ^ The position refers to the top of the text.+ -- ^ The position refers to the top of the text, calculated relative to+ -- the font size. Also see 'AlignLineTop'. | AlignMiddle -- ^ The middle of the text. | AlignBottom -- ^ The position refers to the bottom of the text, including- -- descenders, like g.+ -- descenders, like g. This is calculated relative to the+ -- font size. Also see 'AlignLineBottom'. | AlignBaseline -- ^ The position refers to the baseline of the text (so it does -- not include descenders). This maps to the Vega-Lite -- @\"alphabetic\"@ value. -- -- @since 0.6.0.0+ | AlignLineTop+ -- ^ Similar to 'AlignTop', but relative to the line height, not font size.+ --+ -- This was added in Vega-Lite 4.6.0.+ --+ -- @since 0.7.0.0+ | AlignLineBottom+ -- ^ Similar to 'AlignBottom', but relative to the line height, not font size.+ --+ -- This was added in Vega-Lite 4.6.0.+ --+ -- @since 0.7.0.0 hAlignLabel :: HAlign -> T.Text hAlignLabel AlignLeft = "left"@@ -603,6 +617,8 @@ vAlignLabel AlignMiddle = "middle" vAlignLabel AlignBottom = "bottom" vAlignLabel AlignBaseline = "alphabetic"+vAlignLabel AlignLineTop = "line-top"+vAlignLabel AlignLineBottom = "line-bottom" {-| @@ -1056,6 +1072,9 @@ -- 'Graphics.Vega.VegaLite.BLChannel', 'Graphics.Vega.VegaLite.BLChannelEvent', -- 'Graphics.Vega.VegaLite.ByChannel', and 'Graphics.Vega.VegaLite.Encodings' -- constructors.+--+-- Changed in @0.7.0.0@: the @ChTooltip@ channel was removed as it was+-- dropped in Vega-Lite 4.0. -- assuming this is based on schema 3.3.0 #/definitions/SingleDefUnitChannel @@ -1094,8 +1113,6 @@ -- ^ @since 0.4.0.0 | ChText -- ^ @since 0.4.0.0- | ChTooltip- -- ^ @since 0.4.0.0 channelLabel :: Channel -> T.Text@@ -1120,7 +1137,6 @@ channelLabel ChStrokeWidth = "strokeWidth" channelLabel ChStrokeOpacity = "strokeOpacity" channelLabel ChText = "text"-channelLabel ChTooltip = "tooltip" {-|
src/Graphics/Vega/VegaLite/Mark.hs view
@@ -26,6 +26,7 @@ , ColorGradient(..) , GradientProperty(..) , TextDirection(..)+ , BlendMode(..) -- not for external export , mprops_@@ -60,6 +61,7 @@ , VAlign , fromColor , fromDS+ , fromT , cursorLabel , fontWeightSpec , orientationSpec@@ -243,6 +245,12 @@ -- -- The ideal value for this is either @0@ (preferred by statisticians) -- or @1@ (the Vega-Lite default value, D3 example style).+ | MBlend BlendMode+ -- ^ How should the item be blended with its background?+ --+ -- Added in Vega-Lite 4.6.0.+ --+ -- @since 0.7.0.0 | MBorders [MarkProperty] -- ^ Border properties for an 'ErrorBand' mark. See also 'MNoBorders'. --@@ -627,6 +635,10 @@ markProperty (MBinSpacing x) = "binSpacing" .= x +-- only available in AreaConfig, BarConfig, LineConfig, MarkConfig,+-- MarkDef, OverlayMarkDef, RectConfig, TickConfig+markProperty (MBlend bl) = "blend" .= blendModeSpec bl+ -- only available in ErrorBand[Config|Def], PartsMixins<ErrorBandPart> markProperty MNoBorders = "borders" .= False markProperty (MBorders mps) = mprops_ "borders" mps@@ -1018,3 +1030,71 @@ textdirLabel :: TextDirection -> T.Text textdirLabel LTR = "ltr" textdirLabel RTL = "rtl"+++-- | The blend mode for drawing an item on its background.+--+-- This is based on CSS <https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode mix-blend-mode>+-- and the default is 'BMNormal' (at least for SVG output).+--+-- Added in Vega-Lite 4.6.0.+--+-- It is currently unclear how this works with canvas outputs+-- (see Vega-Lite <https://github.com/vega/vega-lite/pull/6033 #6033>+-- and <https://github.com/vega/vega-lite/issues/6100 #6100>+-- for more information).+--+-- @since 0.7.0.0++data BlendMode+ = BMNormal+ -- ^ @normal@ mode (this maps to @null@ in Vega-Lite).+ | BMMultiply+ -- ^ @multiply@ mode.+ | BMScreen+ -- ^ @screen@ mode.+ | BMOverlay+ -- ^ @overlay@ mode.+ | BMDarken+ -- ^ @daren@ mode.+ | BMLighten+ -- ^ @lighten@ mode.+ | BMColorDodge+ -- ^ @color-dodge@ mode.+ | BMColorBurn+ -- ^ @color-burn@ mode.+ | BMHardLight+ -- ^ @hard-light@ mode.+ | BMSoftLight+ -- ^ @soft-light@ mode.+ | BMDifference+ -- ^ @difference@ mode.+ | BMExclusion+ -- ^ @exclusion@ mode.+ | BMHue+ -- ^ @hue@ mode.+ | BMSaturation+ -- ^ @saturation@ mode.+ | BMColor+ -- ^ @color@ mode.+ | BMLuminosity+ -- ^ @luminosity@ mode.+++blendModeSpec :: BlendMode -> VLSpec+blendModeSpec BMNormal = A.Null+blendModeSpec BMMultiply = fromT "multiply"+blendModeSpec BMScreen = fromT "screen"+blendModeSpec BMOverlay = fromT "overlay"+blendModeSpec BMDarken = fromT "darken"+blendModeSpec BMLighten = fromT "lighten"+blendModeSpec BMColorDodge = fromT "color-dodge"+blendModeSpec BMColorBurn = fromT "color-burn"+blendModeSpec BMHardLight = fromT "hard-light"+blendModeSpec BMSoftLight = fromT "soft-light"+blendModeSpec BMDifference = fromT "difference"+blendModeSpec BMExclusion = fromT "exclusion"+blendModeSpec BMHue = fromT "hue"+blendModeSpec BMSaturation = fromT "saturation"+blendModeSpec BMColor = fromT "color"+blendModeSpec BMLuminosity = fromT "luminosity"
src/Graphics/Vega/VegaLite/Scale.hs view
@@ -29,10 +29,14 @@ import Graphics.Vega.VegaLite.Foundation- ( fromT+ ( Channel+ , FieldName+ , fromT+ , channelLabel ) import Graphics.Vega.VegaLite.Specification ( VLSpec+ , SelectionLabel ) import Graphics.Vega.VegaLite.Time ( DateTime@@ -55,8 +59,25 @@ -- ^ String values that define a scale domain. | DDateTimes [[DateTime]] -- ^ Date-time values that define a scale domain.- | DSelection T.Text+ | DSelection SelectionLabel -- ^ Scale domain based on a named interactive selection.+ -- See also 'DSelectionField' and 'DSelectionChannel', which should+ -- be used when a selection is+ -- [projected](https://vega.github.io/vega-lite/docs/project.html)+ -- over multiple fields or encodings.+ --+ -- In @0.7.0.0@ the argument type was changed to @SelectionLabel@+ -- (which is a type synonym for @Text@).+ | DSelectionField SelectionLabel FieldName+ -- ^ Use the given selection /and/ associated field, when the selection+ -- is projected over multiple fields or encodings.+ --+ -- @since 0.7.0.0+ | DSelectionChannel SelectionLabel Channel+ -- ^ Use the given selection /and/ associated encoding, when the selection+ -- is projected over multiple fields or encodings.+ --+ -- @since 0.7.0.0 | DUnionWith ScaleDomain -- ^ Combine the domain of the data with the provided domain. --@@ -79,6 +100,10 @@ scaleDomainSpec (DDateTimes dts) = toJSON (map (object . map dateTimeProperty) dts) scaleDomainSpec (DStrings cats) = toJSON (map toJSON cats) scaleDomainSpec (DSelection selName) = object ["selection" .= selName]+scaleDomainSpec (DSelectionField selName field) = object [ "selection" .= selName+ , "field" .= field ]+scaleDomainSpec (DSelectionChannel selName ch) = object [ "selection" .= selName+ , "encoding" .= channelLabel ch ] scaleDomainSpec (DUnionWith sd) = object ["unionWith" .= scaleDomainSpec sd] scaleDomainSpec Unaggregated = "unaggregated"
src/Graphics/Vega/VegaLite/Time.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-|@@ -8,7 +7,7 @@ Maintainer : dburke.gw@gmail.com Stability : unstable-Portability : CPP, OverloadedStrings+Portability : OverloadedStrings Time-related types. @@ -29,10 +28,6 @@ import qualified Data.Text as T import Data.Aeson ((.=), object)--#if !(MIN_VERSION_base(4, 12, 0))-import Data.Monoid ((<>))-#endif -- added in base 4.8.0.0 / ghc 7.10.1 import Numeric.Natural (Natural)
src/Graphics/Vega/VegaLite/Transform.hs view
@@ -140,6 +140,12 @@ -- ^ Minimum field value to be used in an aggregation operation. | Missing -- ^ Count of @null@ or @undefined@ field value to be used in an aggregation operation.+ | Product+ -- ^ Product of field values to be used in an aggregate operation.+ --+ -- This was added in Vega-Lite 4.6.0.+ --+ -- @since 0.7.0.0 | Q1 -- ^ Lower quartile boundary of field values to be used in an aggregation operation. | Q3@@ -177,6 +183,7 @@ operationSpec Median = "median" operationSpec Min = "min" operationSpec Missing = "missing"+operationSpec Product = "product" operationSpec Q1 = "q1" operationSpec Q3 = "q3" operationSpec Stderr = "stderr"
tests/AxisTests.hs view
@@ -37,7 +37,10 @@ , ("axisstyleempty", axisStyleEmpty) , ("axisstyleemptyx", axisStyleEmptyX) , ("axisstylex", axisStyleX)+ , ("axisstylexastyle", axisStyleXAStyle) , ("axisstylexy", axisStyleXY)+ , ("singleline", singleLine)+ , ("multiline", multiLine) ] @@ -106,17 +109,17 @@ in toVegaLite vs plotCfg :: [ConfigurationProperty]-plotCfg = [ AxisQuantitative [ DomainColor "orange"- , GridColor "seagreen"- , LabelFont "Comic Sans MS"- , LabelOffset 10- , TickOffset 10- ]- , AxisTemporal [ DomainColor "brown"- , DomainDash [4, 2]- , Grid False- , LabelColor "purple"- ]+plotCfg = [ AxisQuantitative AxXY [ DomainColor "orange"+ , GridColor "seagreen"+ , LabelFont "Comic Sans MS"+ , LabelOffset 10+ , TickOffset 10+ ]+ , AxisTemporal AxXY [ DomainColor "brown"+ , DomainDash [4, 2]+ , Grid False+ , LabelColor "purple"+ ] , PointStyle [ MStroke "black" , MStrokeOpacity 0.4 , MStrokeWidth 1@@ -305,6 +308,24 @@ ] +-- check AStyle; should give same look as axisStyleX+axisStyleXAStyle :: VegaLite+axisStyleXAStyle =+ let cfg = configure+ . configuration (AxisNamedStyles [("x-style", [ AxDomainColor "orange"+ , AxGridColor "lightgreen"+ , AxLabelExpr xexpr ])])+ . configuration (AxisX [AStyle ["x-style"]])++ xexpr = "if (datum.value <= 100, 'low:' + datum.label, 'high:' + datum.label)"++ in toVegaLite [ cfg []+ , carData+ , carEnc [] []+ , mark Point []+ ]++ axisStyleXY :: VegaLite axisStyleXY = let cfg = configure@@ -324,4 +345,34 @@ , carData , carEnc [AxStyle ["x-style"]] [AxStyle ["y-style"]] , mark Point []+ ]+++singleLine :: VegaLite+singleLine =+ let xOpts = [ AxLabelExpr "datum.label + ' horses'" ]+ yOpts = [ AxLabelExpr "datum.label+' mpg'" ]++ in toVegaLite [ carData+ , carEnc xOpts yOpts+ , mark Point []+ ]+++multiLine :: VegaLite+multiLine =+ let xOpts = [ AxLabelExpr "datum.label + ' horses'"+ , AxLabelLineHeight 22+ , AxLabelFontSize 11+ ]+ yOpts = [ AxLabelExpr "datum.label+' mpg'"+ , AxLabelFontSize 22 ]++ in toVegaLite [ carData+ , carEnc xOpts yOpts+ , mark Point []+ , configure+ . configuration (LineBreakStyle " ")+ . configuration (Axis [LabelLineHeight 20])+ $ [] ]
tests/ConfigTests.hs view
@@ -6,10 +6,6 @@ -- - Padding has been removed as the resulting spec does not validate -- against v3.3.0 ----- - the vbTest output is not valid since the spec description says that--- style can be a string or array-of-strings, but the type is only--- string.--- module ConfigTests (testSpecs) where @@ -38,6 +34,7 @@ , ("titleCfg1", titleCfg1) , ("titleCfg2", titleCfg2) , ("titleCfg3", titleCfg3)+ , ("breaklinecfg", breakLineCfg) ] @@ -318,3 +315,22 @@ ( [ cfg [] , title "Car\nScatter" [ subtitle ] ] ++ titleOpts )+++breakLineCfg :: VegaLite+breakLineCfg =+ let dvals = dataFromColumns []+ . dataColumn "x" (Numbers [5, 10, 15])+ . dataColumn "y" (Numbers [10, 5, 30])+ . dataColumn "l" (Strings ["xXx", "x x", "xxXxXxx"])++ enc = encoding+ . position X [PName "x", PmType Quantitative]+ . position Y [PName "y", PmType Quantitative]+ . text [TName "l", TmType Nominal]++ in toVegaLite [ configure (configuration (LineBreakStyle "X") [])+ , dvals []+ , enc []+ , mark Text []+ ]
+ tests/FilterTests.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE OverloadedStrings #-}++module FilterTests (testSpecs) where++import Data.Function ((&))++import Graphics.Vega.VegaLite++import Prelude hiding (filter)++testSpecs :: [(String, VegaLite)]+testSpecs = [ ("datedate", dateDate)+ , ("datedatell", dateDateLL)+ , ("datedateul", dateDateUL)+ , ("datenumber", dateNumber)+ , ("datenumberll", dateNumberLL)+ , ("datenumberul", dateNumberUL)+ ]++stockData :: Data+stockData = dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" []++goog :: [TransformSpec] -> PropertySpec+goog = transform . filter (FExpr "datum.symbol === 'GOOG'")++baseEnc :: [EncodingSpec] -> PropertySpec+baseEnc = encoding+ . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%m %Y" ] ]+ . position Y [ PName "price", PmType Quantitative ]+++dateFilter :: Filter -> VegaLite+dateFilter fexpr =+ toVegaLite [ stockData+ , goog+ . filter fexpr+ $ []+ , width 400+ , mark Line []+ , baseEnc []+ , configure+ . configuration (Axis [DomainColor "#ddd", TickColor "#ddd"])+ . configuration (LineBreakStyle " ")+ $ []+ ]++ +dateFilterNumbers :: Maybe Double -> Maybe Double -> VegaLite+dateFilterNumbers mlo mhi =+ let yearRange = FRange "date" frange+ & FilterOpTrans (MTimeUnit Year)+ & FCompose++ frange = case (mlo, mhi) of+ (Just lo, Just hi) -> NumberRange lo hi+ (Just lo, Nothing) -> NumberRangeLL lo+ (Nothing, Just hi) -> NumberRangeUL hi+ _ -> error "Internal error"++ in dateFilter yearRange+++dateFilterDates :: Maybe Int -> Maybe Int -> VegaLite+dateFilterDates mlo mhi =+ let yearRange = FRange "date" frange++ frange = case (mlo, mhi) of+ (Just lo, Just hi) -> DateRange [DTYear lo] [DTYear hi]+ (Just lo, Nothing) -> DateRange [DTYear lo] []+ (Nothing, Just hi) -> DateRange [] [DTYear hi]+ _ -> error "Internal error"++ in dateFilter yearRange+++dateDate, dateDateLL, dateDateUL :: VegaLite+dateDate = dateFilterDates (Just 2006) (Just 2007)+dateDateLL = dateFilterDates (Just 2006) Nothing+dateDateUL = dateFilterDates Nothing (Just 2007)+++dateNumber, dateNumberLL, dateNumberUL :: VegaLite+dateNumber = dateFilterNumbers (Just 2006) (Just 2007)+dateNumberLL = dateFilterNumbers (Just 2006) Nothing+dateNumberUL = dateFilterNumbers Nothing (Just 2007)
tests/Gallery/Facet.hs view
@@ -20,6 +20,7 @@ , ("facet5", facet5) , ("facet6", facet6) , ("facet7", facet7)+ , ("trellisareaseattle", trellisAreaSeattle) ] @@ -214,3 +215,52 @@ . configuration (ViewStyle [ ViewNoStroke ]) in toVegaLite [ des, width 300, height 50, cfg [], res [], dvals [], mark Area [], enc [] ]+++-- https://vega.github.io/vega-lite/examples/trellis_area_seattle.html+--+trellisAreaSeattle :: VegaLite+trellisAreaSeattle =+ let desc = description "Average temps in Seattle, by hour"++ ylabels = "hours(datum.value) == 0 ? 'Midnight' : hours(datum.value) == 12 ? 'Noon' : timeFormat(datum.value, '%I:%M %p')"++ plot = asSpec [ width 800+ , height 25+ , viewBackground [VBNoStroke]+ , mark Area []+ , encoding+ . position X [ PName "date"+ , PmType Temporal+ , PTitle "Month"+ , PAxis [AxFormat "%b"]+ ]+ . position Y [ PName "temp"+ , PmType Quantitative+ , PScale [SZero False]+ , PAxis [AxNoTitle, AxLabels False, AxTicks False]+ ]+ $ []+ ]++ in toVegaLite [ desc+ , title "Seattle Annual Temperatures" []+ , dataFromUrl "https://vega.github.io/vega-lite/data/seattle-temps.csv" []+ , transform+ (calculateAs "(hours(datum.date) + 18) % 24" "order" [])+ , spacing 1 -- NOTE: can not specify this is just for row+ , configure (configuration (Axis [Grid False, Domain False]) [])+ , facet [ RowBy [ FName "date"+ , FmType Nominal+ , FTimeUnit Hours+ , FSort [ByFieldOp "order" Max] -- don't want an operation+ , FHeader [ HLabelAngle 0+ , HLabelPadding 2+ , HTitlePadding (-4)+ , HLabelAlign AlignLeft+ , HLabelExpr ylabels+ ]+ ]+ ]+ , specification plot+ ]
tests/Gallery/Label.hs view
@@ -755,33 +755,40 @@ baselines :: VegaLite baselines = let dvals = dataFromColumns []- . dataColumn "x" (Numbers [ 10, 20 ])- . dataColumn "y" (Numbers [ 10, 20 ])+ . dataColumn "x" (Numbers [ 5, 30 ])+ . dataColumn "y" (Numbers [ 5, 30 ]) ax t n = position t [ PName n , PmType Quantitative , PScale [ SNice (IsNice False)- , SDomain (DNumbers [5, 25])+ , SDomain (DNumbers [-10, 40]) ]- , PAxis [ AxNoTitle ]+ , PAxis [ AxNoTitle+ ] ] enc = encoding . ax X "x" . ax Y "y"- . text [ TString "Xxgq" ]+ . text [ TStrings ["Xxgq", "qTpP"] ] plot l a = asSpec [ enc [], title l [], mark Text [ MBaseline a ] ] + -- plots are ordered left to right, top to bottom plots = vlConcat [ plot "top" AlignTop+ , plot "line-top" AlignLineTop , plot "middle" AlignMiddle , plot "baseline" AlignBaseline , plot "bottom" AlignBottom+ , plot "line-bottom" AlignLineBottom ] in toVegaLite [ dvals [] , configure- . configuration (MarkStyle [ MFontSize 20 ])+ . configuration (MarkStyle [ MFontSize 20+ , MFontStyle "italic"+ , MLineHeight 30 ])+ . configuration (TitleStyle [ TFont "Comic Sans MS" ]) $ [] , columns 2 , plots
tests/Gallery/Line.hs view
@@ -11,6 +11,7 @@ import Graphics.Vega.VegaLite import Prelude hiding (filter, lookup, repeat)+import Data.Function ((&)) testSpecs :: [(String, VegaLite)]@@ -26,72 +27,51 @@ , ("line10", line10) , ("line11", line11) , ("line12", line12)+ , ("conditionalaxis", conditionalAxis) ] -line1 :: VegaLite-line1 =- let- des =- description "Google's stock price over time."+stockData :: Data+stockData = dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" [] - trans =- transform . filter (FExpr "datum.symbol === 'GOOG'")+goog :: [TransformSpec] -> PropertySpec+goog = transform . filter (FExpr "datum.symbol === 'GOOG'") - enc =- encoding- . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%Y" ] ]- . position Y [ PName "price", PmType Quantitative ]- in+baseEnc :: [EncodingSpec] -> PropertySpec+baseEnc = encoding+ . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%Y" ] ]+ . position Y [ PName "price", PmType Quantitative ]+++line1 :: VegaLite+line1 = toVegaLite- [ des- , dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" []- , trans []+ [ description "Google's stock price over time."+ , stockData+ , goog [] , mark Line []- , enc []+ , baseEnc [] ] line2 :: VegaLite line2 =- let- des =- description "Google's stock price over time with point markers."-- trans =- transform . filter (FExpr "datum.symbol === 'GOOG'")-- enc =- encoding- . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%Y" ] ]- . position Y [ PName "price", PmType Quantitative ]- in toVegaLite- [ des- , dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" []- , trans []+ [ description "Google's stock price over time with point markers."+ , stockData+ , goog [] , mark Line [ MColor "green", MPoint (PMMarker [ MColor "purple" ]) ]- , enc []+ , baseEnc [] ] line3 :: VegaLite line3 =- let- des =- description "Stock prices of 5 tech companies over time."-- enc =- encoding- . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%Y" ] ]- . position Y [ PName "price", PmType Quantitative ]- . color [ MName "symbol", MmType Nominal ]- in toVegaLite- [ des- , dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" []+ [ description "Stock prices of 5 tech companies over time."+ , stockData , mark Line []- , enc []+ , baseEnc (color [ MName "symbol", MmType Nominal ] []) ] @@ -118,24 +98,12 @@ line5 :: VegaLite line5 =- let- des =- description "Google's stock price over time (quantized as a step-chart)."-- trans =- transform . filter (FExpr "datum.symbol === 'GOOG'")-- enc =- encoding- . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%Y" ] ]- . position Y [ PName "price", PmType Quantitative ]- in toVegaLite- [ des- , dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" []- , trans []+ [ description "Google's stock price over time (quantized as a step-chart)."+ , stockData+ , goog [] , mark Line [ MInterpolate StepAfter ]- , enc []+ , baseEnc [] ] @@ -145,20 +113,13 @@ des = description "Google's stock price over time (smoothed with monotonic interpolation)." - trans =- transform . filter (FExpr "datum.symbol === 'GOOG'")-- enc =- encoding- . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%Y" ] ]- . position Y [ PName "price", PmType Quantitative ] in toVegaLite [ des- , dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" []- , trans []+ , stockData+ , goog [] , mark Line [ MInterpolate Monotone ]- , enc []+ , baseEnc [] ] @@ -189,15 +150,13 @@ description "Stock prices of five tech companies over time double encoding price with vertical position and line thickness." enc =- encoding- . position X [ PName "date", PmType Temporal, PAxis [ AxFormat "%Y" ] ]- . position Y [ PName "price", PmType Quantitative ]- . size [ MName "price", MmType Quantitative ]- . color [ MName "symbol", MmType Nominal ]+ baseEnc+ . size [ MName "price", MmType Quantitative ]+ . color [ MName "symbol", MmType Nominal ] in toVegaLite [ des- , dataFromUrl "https://vega.github.io/vega-lite/data/stocks.csv" []+ , stockData , mark Trail [] , enc [] ]@@ -371,3 +330,49 @@ asSpec [ encCos [], mark Line [ MStroke "firebrick" ] ] in toVegaLite [ des, width 300, height 150, dvals, trans [], layer [ specSin, specCos ] ]+++-- From https://vega.github.io/vega-lite/examples/line_conditional_axis.html+-- added in Vega-Lite 4.6.0+--+conditionalAxis :: VegaLite+conditionalAxis =+ let enc = encoding+ . position Y [PName "price", PmType Quantitative]+ . position X [PName "date", PmType Temporal, PAxis axOpts]++ axOpts = [ AxTickCount 8+ , AxLabelAlign AlignLeft+ , AxLabelExpr expr+ , AxLabelOffset 4+ , AxLabelPadding (-24)+ , AxTickSize 30+ , AxDataCondition+ cond+ (CAxGridDash [] [2, 2])+ , AxDataCondition+ cond+ (CAxTickDash [] [2, 2])+ ]++ expr = "[timeFormat(datum.value, '%b'), timeFormat(datum.value, '%m') == '01' ? timeFormat(datum.value, '%Y') : '']"++ cond = FEqual "value" (Number 1)+ & FilterOpTrans (MTimeUnit Month)++ yearRange = FRange "date" (NumberRange 2006 2007)+ & FilterOpTrans (MTimeUnit Year)+ & FCompose++ in toVegaLite [ description "Line chart with conditional axis ticks, labels, and grid."+ , stockData+ , goog+ . filter yearRange+ $ []+ , width 400+ , mark Line []+ , enc []+ , configure+ . configuration (Axis [DomainColor "#ddd", TickColor "#ddd"])+ $ []+ ]
tests/Gallery/Multi.hs view
@@ -19,6 +19,7 @@ , ("multi6", multi6) , ("multi7", multi7) , ("multi8", multi8)+ , ("selectandzoom", selectAndZoom) ] @@ -493,3 +494,40 @@ spec2 = asSpec [ width 963, height 100, enc2 [], mark Bar [] ] in toVegaLite [ dvals, trans [], vConcat [ spec1, spec2 ] ]+++-- interval selection in one plot to zoom in in second plot.+--+selectAndZoom :: VegaLite+selectAndZoom =+ let plot1 = asSpec [ encoding+ . position X [ PName "theta", PmType Quantitative, PAxis [] ]+ . position Y [ PName "y", PmType Quantitative, PAxis [] ]+ $ []+ , mark Line []+ , selection+ . select "brush" Interval [ Encodings [ChX, ChY]+ , SInitInterval (Just xr) (Just yr)+ ]+ $ []+ ]++ xr = (Number 0.2, Number 6)+ yr = (Number (-0.8), Number 0.8)++ xscale = [ SDomain (DSelectionField "brush" "theta") ]+ yscale = [ SDomain (DSelectionChannel "brush" ChY) ]++ plot2 = asSpec [ encoding+ . position X [ PName "theta", PmType Quantitative, PScale xscale ]+ . position Y [ PName "y", PmType Quantitative, PScale yscale ]+ $ []+ , mark Circle []+ ]++ in toVegaLite [ dataSequenceAs 0 6.28 0.1 "theta"+ , transform+ . calculateAs "cos(datum.theta)" "y"+ $ []+ , vConcat [plot1, plot2]+ ]
+ tests/MarkTests.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}++--+-- random mark-related tests+--++module MarkTests (testSpecs) where++import qualified Data.Text as T++import Data.List (intercalate)++#if !(MIN_VERSION_base(4, 12, 0))+import Data.Monoid ((<>))+#endif++import Text.Printf (printf)++import Graphics.Vega.VegaLite++testSpecs :: [(String, VegaLite)]+testSpecs = [("blendmode", blendMode)]+++-- How does blend-mode work (added in Vega-Lite 4.6.0)?+-- This is based on+-- https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode#Examples+--++blendData :: Data+blendData =+ dataFromColumns []+ . dataColumn "x" (Numbers [0])+ . dataColumn "y" (Numbers [0])+ $ []+++-- rotate an ellipse about the origin; it would be better if I learned+-- how to use the arc path segment+--+-- angle in degrees+ellipse :: Double -> Symbol+ellipse ang =+ let rad = ang * pi / 180+ cosRot = cos rad+ sinRot = sin rad+ + rmajor = 1.0+ rminor = 0.3++ p = printf "%.2f"+ + -- could learn to use the Arc path segment, but just do it manually+ pair t =+ let x = rmajor * cos t * cosRot - rminor * sin t * sinRot+ y = rmajor * cos t * sinRot + rminor * sin t * cosRot+ in "L " <> p x <> " " <> p y++ thetas = [0, 0.25 .. 2 * pi]+ path = intercalate " " (map pair thetas)+ + in SymPath (T.pack path)+ ++-- Is there a better way to do this?+blendMode :: VegaLite+blendMode =+ let ax t f = position t [ PName f+ , PmType Quantitative+ , PScale [SDomain (DNumbers [-5, 5])]+ ]++ -- randomly trying to get similar results to the Mozilla page+ props 0 = [GrX1 0.5, GrX2 0.5, GrY1 1, GrY2 0] + props 1 = [GrX1 0.7, GrX2 0.3, GrY1 0.1, GrY2 1]+ props _ = [GrX1 1, GrX2 0, GrY1 1, GrY2 0]+ + gradient f =+ let c | f == 0 = "rgb(0,255,0)"+ | f == 1 = "rgb(255,0,0)"+ | otherwise = "rgb(0,0,255)"+ + in MFillGradient GrLinear [(0, "white"), (1, c)] (props f)++ lyr bm f =+ let a | f == 0 = 0+ | f == 1 = 45+ | otherwise = -45+ + in asSpec [ mark Point [ MShape (ellipse a)+ , MBlend bm+ , gradient f+ ]+ ]++ createLayer (bm, ttl) =+ asSpec [ encoding . ax X "x" . ax Y "y" $ []+ , layer (map (lyr bm) [0::Int .. 2])+ , title ttl []+ ]++ layers = map createLayer [ (BMNormal, "Normal")+ , (BMMultiply, "Multiply")+ , (BMScreen, "Screen")+ , (BMOverlay, "Overlay")+ , (BMDarken, "Darken")+ , (BMLighten, "Lighten")+ , (BMColorDodge, "Color-Dodge")+ , (BMColorBurn, "Color-Burn")+ , (BMHardLight, "Hard-Light")+ , (BMSoftLight, "Soft-Light")+ , (BMDifference, "Difference")+ , (BMExclusion, "Exclusion")+ , (BMHue, "Hue")+ , (BMSaturation, "Saturation")+ , (BMColor, "Color")+ , (BMLuminosity, "Luminosity")+ ]+ + in toVegaLite [ configure+ . configuration (Axis [Domain False, Labels False, Ticks False, NoTitle])+ . configuration (PointStyle [MOpacity 1, MSize 40000, MStroke ""])+ -- note the interesting background+ . configuration (BackgroundStyle "rgba(255,255,255,0)")+ $ []+ , blendData+ , columns 4+ , vlConcat layers+ ]
tests/Test.hs view
@@ -46,11 +46,13 @@ import qualified DataTests as DT import qualified EncodingTests import qualified FillStrokeTests as FST+import qualified FilterTests import qualified GeoTests as GT import qualified HyperlinkTests as HT import qualified ImageTests as ImT import qualified InteractionTests as IT import qualified LegendTests as LT+import qualified MarkTests import qualified NullTests as NT import qualified PositionTests as PT import qualified ProjectionTests as PjT@@ -119,11 +121,13 @@ , toTests "Data" "data" DT.testSpecs , toTests "Encoding" "encoding" EncodingTests.testSpecs , toTests "FillStroke" "fillstroke" FST.testSpecs+ , toTests "Filter" "filter" FilterTests.testSpecs , toTests "Geo" "geo" GT.testSpecs , toTests "Hyperlink" "hyperlink" HT.testSpecs , toTests "Image" "image" ImT.testSpecs , toTests "Interaction" "interaction" IT.testSpecs , toTests "Legend" "legend" LT.testSpecs+ , toTests "Mark" "mark" MarkTests.testSpecs , toTests "Null" "null" NT.testSpecs , toTests "Position" "position" PT.testSpecs , toTests "Projection" "projection" PjT.testSpecs
tests/TransformTests.hs view
@@ -35,6 +35,7 @@ , ("weatherbymonth", weatherByMonth) , ("weatherbytwomonths", weatherByTwoMonths) , ("distances", distances)+ , ("aggregates", aggregates) , ("weathermaxbins", weatherMaxBins) , ("windowplot", windowPlot) , ("joinaggregateplot", joinAggregatePlot)@@ -380,6 +381,30 @@ , PAggregate Sum ] in toVegaLite [ dvals [], enc [], mark Bar [] ]+++aggregates :: VegaLite+aggregates =+ let dvals = dataFromColumns []+ . dataColumn "xs" (Strings ["A", "A", "B", "A", "B", "B", "C", "C"])+ . dataColumn "ys" (Numbers [1, 2, 2, 2, 3, 2, 4, 8])++ enc op = encoding (position Y [ PName "ys"+ , PAxis [AxTitle "Y"]+ , PmType Quantitative+ , PAggregate op ] [])++ lyr1 = asSpec [ enc Sum, mark Bar [MStroke "black"] ]+ lyr2 = asSpec [ enc Product, mark Line [MColor "brown"] ]+ lyr3 = asSpec [ enc Max, mark Point [MColor "orange"] ]+ lyr4 = asSpec [ enc Distinct, mark Square [MColor "cyan"] ]++ axOpts = PAxis [AxTitle "X", AxLabelAngle 0]++ in toVegaLite [ dvals []+ , encoding (position X [PName "xs", axOpts, PmType Ordinal] [])+ , layer [lyr1, lyr2, lyr3, lyr4]+ ] activityData :: Data
+ tests/specs/axis/axisstylexastyle.vl view
@@ -0,0 +1,34 @@+{+ "config": {+ "style": {+ "x-style": {+ "domainColor": "orange",+ "labelExpr": "if (datum.value <= 100, 'low:' + datum.label, 'high:' + datum.label)",+ "gridColor": "lightgreen"+ }+ },+ "axisX": {+ "style": "x-style"+ }+ },+ "mark": "point",+ "data": {+ "url": "https://vega.github.io/vega-lite/data/cars.json"+ },+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json",+ "encoding": {+ "color": {+ "field": "Origin",+ "type": "nominal",+ "legend": null+ },+ "x": {+ "field": "Horsepower",+ "type": "quantitative"+ },+ "y": {+ "field": "Miles_per_Gallon",+ "type": "quantitative"+ }+ }+}
+ tests/specs/axis/multiline.vl view
@@ -0,0 +1,37 @@+{+ "config": {+ "axis": {+ "labelLineHeight": 20+ },+ "lineBreak": " "+ },+ "mark": "point",+ "data": {+ "url": "https://vega.github.io/vega-lite/data/cars.json"+ },+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json",+ "encoding": {+ "color": {+ "field": "Origin",+ "type": "nominal",+ "legend": null+ },+ "x": {+ "field": "Horsepower",+ "type": "quantitative",+ "axis": {+ "labelExpr": "datum.label + ' horses'",+ "labelLineHeight": 22,+ "labelFontSize": 11+ }+ },+ "y": {+ "field": "Miles_per_Gallon",+ "type": "quantitative",+ "axis": {+ "labelExpr": "datum.label+' mpg'",+ "labelFontSize": 22+ }+ }+ }+}
+ tests/specs/axis/singleline.vl view
@@ -0,0 +1,28 @@+{+ "mark": "point",+ "data": {+ "url": "https://vega.github.io/vega-lite/data/cars.json"+ },+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json",+ "encoding": {+ "color": {+ "field": "Origin",+ "type": "nominal",+ "legend": null+ },+ "x": {+ "field": "Horsepower",+ "type": "quantitative",+ "axis": {+ "labelExpr": "datum.label + ' horses'"+ }+ },+ "y": {+ "field": "Miles_per_Gallon",+ "type": "quantitative",+ "axis": {+ "labelExpr": "datum.label+' mpg'"+ }+ }+ }+}
+ tests/specs/config/breaklinecfg.vl view
@@ -0,0 +1,40 @@+{+ "config": {+ "lineBreak": "X"+ },+ "mark": "text",+ "data": {+ "values": [+ {+ "l": "xXx",+ "x": 5,+ "y": 10+ },+ {+ "l": "x x",+ "x": 10,+ "y": 5+ },+ {+ "l": "xxXxXxx",+ "x": 15,+ "y": 30+ }+ ]+ },+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json",+ "encoding": {+ "text": {+ "field": "l",+ "type": "nominal"+ },+ "x": {+ "field": "x",+ "type": "quantitative"+ },+ "y": {+ "field": "y",+ "type": "quantitative"+ }+ }+}
+ tests/specs/gallery/facet/trellisareaseattle.vl view
@@ -0,0 +1,69 @@+{+ "transform": [+ {+ "as": "order",+ "calculate": "(hours(datum.date) + 18) % 24"+ }+ ],+ "config": {+ "axis": {+ "domain": false,+ "grid": false+ }+ },+ "data": {+ "url": "https://vega.github.io/vega-lite/data/seattle-temps.csv"+ },+ "spec": {+ "height": 25,+ "mark": "area",+ "width": 800,+ "view": {+ "stroke": null+ },+ "encoding": {+ "x": {+ "field": "date",+ "title": "Month",+ "type": "temporal",+ "axis": {+ "format": "%b"+ }+ },+ "y": {+ "field": "temp",+ "scale": {+ "zero": false+ },+ "type": "quantitative",+ "axis": {+ "labels": false,+ "title": null,+ "ticks": false+ }+ }+ }+ },+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json",+ "facet": {+ "row": {+ "field": "date",+ "timeUnit": "hours",+ "header": {+ "labelAngle": 0,+ "labelExpr": "hours(datum.value) == 0 ? 'Midnight' : hours(datum.value) == 12 ? 'Noon' : timeFormat(datum.value, '%I:%M %p')",+ "titlePadding": -4,+ "labelPadding": 2,+ "labelAlign": "left"+ },+ "sort": {+ "op": "max",+ "field": "order"+ },+ "type": "nominal"+ }+ },+ "title": "Seattle Annual Temperatures",+ "description": "Average temps in Seattle, by hour",+ "spacing": 1+}
tests/specs/gallery/label/baselines.vl view
@@ -1,18 +1,23 @@ { "config": { "mark": {- "fontSize": 20+ "fontStyle": "italic",+ "fontSize": 20,+ "lineHeight": 30+ },+ "title": {+ "font": "Comic Sans MS" } }, "data": { "values": [ {- "x": 10,- "y": 10+ "x": 5,+ "y": 5 }, {- "x": 20,- "y": 20+ "x": 30,+ "y": 30 } ] },@@ -25,14 +30,17 @@ "title": "top", "encoding": { "text": {- "value": "Xxgq"+ "value": [+ "Xxgq",+ "qTpP"+ ] }, "x": { "field": "x", "scale": { "domain": [- 5,- 25+ -10,+ 40 ], "nice": false },@@ -45,8 +53,8 @@ "field": "y", "scale": { "domain": [- 5,- 25+ -10,+ 40 ], "nice": false },@@ -60,19 +68,65 @@ { "mark": { "type": "text",+ "baseline": "line-top"+ },+ "title": "line-top",+ "encoding": {+ "text": {+ "value": [+ "Xxgq",+ "qTpP"+ ]+ },+ "x": {+ "field": "x",+ "scale": {+ "domain": [+ -10,+ 40+ ],+ "nice": false+ },+ "type": "quantitative",+ "axis": {+ "title": null+ }+ },+ "y": {+ "field": "y",+ "scale": {+ "domain": [+ -10,+ 40+ ],+ "nice": false+ },+ "type": "quantitative",+ "axis": {+ "title": null+ }+ }+ }+ },+ {+ "mark": {+ "type": "text", "baseline": "middle" }, "title": "middle", "encoding": { "text": {- "value": "Xxgq"+ "value": [+ "Xxgq",+ "qTpP"+ ] }, "x": { "field": "x", "scale": { "domain": [- 5,- 25+ -10,+ 40 ], "nice": false },@@ -85,8 +139,8 @@ "field": "y", "scale": { "domain": [- 5,- 25+ -10,+ 40 ], "nice": false },@@ -105,14 +159,17 @@ "title": "baseline", "encoding": { "text": {- "value": "Xxgq"+ "value": [+ "Xxgq",+ "qTpP"+ ] }, "x": { "field": "x", "scale": { "domain": [- 5,- 25+ -10,+ 40 ], "nice": false },@@ -125,8 +182,8 @@ "field": "y", "scale": { "domain": [- 5,- 25+ -10,+ 40 ], "nice": false },@@ -145,14 +202,17 @@ "title": "bottom", "encoding": { "text": {- "value": "Xxgq"+ "value": [+ "Xxgq",+ "qTpP"+ ] }, "x": { "field": "x", "scale": { "domain": [- 5,- 25+ -10,+ 40 ], "nice": false },@@ -165,8 +225,51 @@ "field": "y", "scale": { "domain": [- 5,- 25+ -10,+ 40+ ],+ "nice": false+ },+ "type": "quantitative",+ "axis": {+ "title": null+ }+ }+ }+ },+ {+ "mark": {+ "type": "text",+ "baseline": "line-bottom"+ },+ "title": "line-bottom",+ "encoding": {+ "text": {+ "value": [+ "Xxgq",+ "qTpP"+ ]+ },+ "x": {+ "field": "x",+ "scale": {+ "domain": [+ -10,+ 40+ ],+ "nice": false+ },+ "type": "quantitative",+ "axis": {+ "title": null+ }+ },+ "y": {+ "field": "y",+ "scale": {+ "domain": [+ -10,+ 40 ], "nice": false },
+ tests/specs/gallery/line/conditionalaxis.vl view
@@ -0,0 +1,76 @@+{+ "transform": [+ {+ "filter": "datum.symbol === 'GOOG'"+ },+ {+ "filter": {+ "field": "date",+ "timeUnit": "year",+ "range": [+ 2006,+ 2007+ ]+ }+ }+ ],+ "config": {+ "axis": {+ "domainColor": "#ddd",+ "tickColor": "#ddd"+ }+ },+ "mark": "line",+ "data": {+ "url": "https://vega.github.io/vega-lite/data/stocks.csv"+ },+ "width": 400,+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json",+ "encoding": {+ "x": {+ "field": "date",+ "type": "temporal",+ "axis": {+ "tickSize": 30,+ "labelOffset": 4,+ "tickDash": {+ "value": [+ 2,+ 2+ ],+ "condition": {+ "value": [],+ "test": {+ "equal": 1,+ "field": "value",+ "timeUnit": "month"+ }+ }+ },+ "tickCount": 8,+ "gridDash": {+ "value": [+ 2,+ 2+ ],+ "condition": {+ "value": [],+ "test": {+ "equal": 1,+ "field": "value",+ "timeUnit": "month"+ }+ }+ },+ "labelExpr": "[timeFormat(datum.value, '%b'), timeFormat(datum.value, '%m') == '01' ? timeFormat(datum.value, '%Y') : '']",+ "labelPadding": -24,+ "labelAlign": "left"+ }+ },+ "y": {+ "field": "price",+ "type": "quantitative"+ }+ },+ "description": "Line chart with conditional axis ticks, labels, and grid."+}
+ tests/specs/gallery/multi/selectandzoom.vl view
@@ -0,0 +1,78 @@+{+ "transform": [+ {+ "as": "y",+ "calculate": "cos(datum.theta)"+ }+ ],+ "data": {+ "sequence": {+ "as": "theta",+ "start": 0,+ "step": 0.1,+ "stop": 6.28+ }+ },+ "vconcat": [+ {+ "mark": "line",+ "selection": {+ "brush": {+ "init": {+ "x": [+ 0.2,+ 6+ ],+ "y": [+ -0.8,+ 0.8+ ]+ },+ "encodings": [+ "x",+ "y"+ ],+ "type": "interval"+ }+ },+ "encoding": {+ "x": {+ "field": "theta",+ "type": "quantitative",+ "axis": null+ },+ "y": {+ "field": "y",+ "type": "quantitative",+ "axis": null+ }+ }+ },+ {+ "mark": "circle",+ "encoding": {+ "x": {+ "field": "theta",+ "scale": {+ "domain": {+ "field": "theta",+ "selection": "brush"+ }+ },+ "type": "quantitative"+ },+ "y": {+ "field": "y",+ "scale": {+ "domain": {+ "selection": "brush",+ "encoding": "y"+ }+ },+ "type": "quantitative"+ }+ }+ }+ ],+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json"+}
+ tools/GetBetelgeuse.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++{-++Grab Betelgeuse data from the AAVSO site. This is based on+code in https://github.com/hippke/betelbot which is+used to generate the tweets from https://twitter.com/betelbot++The idea is to download a bunch of Vis-band observations+and grab the data from the HTML, convert to a very-simple+JSON format and write to the screen. There is limited+error checking.++-}++import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Char8 as L8+import qualified Data.Text as T+import qualified Network.HTTP.Conduit as NHC++import Data.Aeson (ToJSON(..), encode, defaultOptions, genericToEncoding)++import Data.List (sortBy)+import Data.Function (on)+import Data.Maybe (fromMaybe, mapMaybe)+import Data.Text.Encoding (decodeUtf8)++import GHC.Generics++import Text.HTML.TagSoup (Tag(..)+ , isTagOpenName+ , parseTags+ , partitions)+import Text.Read (readMaybe)++import Numeric.Natural (Natural)+++-- | The given page of results from AAVSO for Betelgeuse+--+-- When ran there were ~3900 results, so at 200 per page that's+-- 20 pages+--+pageURL :: Natural -> String+pageURL p = "https://www.aavso.org/apps/webobs/results/?start=2017-06-01&num_results=200&obs_types=vis+ccd&star=betelgeuse&page=" <> show p++nPages :: Natural+nPages = 20+++-- | Return the given page.+--+getPage :: Natural -> IO T.Text+getPage p = decodeUtf8 . L.toStrict <$> NHC.simpleHttp (pageURL p)+++data Row = Row { jd :: Double+ , date :: T.Text+ , magnitude :: Double+ , magErr :: Maybe Double+ , filterName :: T.Text+ , observer :: T.Text+ } deriving (Generic, Show)++instance ToJSON Row where+ toEncoding = genericToEncoding defaultOptions++-- Extract rows+--+-- The data structure is regular, but complicated. I am going+-- to ignore the "extra" data for each observation.+--+extractRows :: [Tag T.Text] -> [Row]+extractRows tgs =+ let rows = partitions (isTagOpenName "tr") tgs+ in mapMaybe extractRow rows +++isObs :: [(T.Text, T.Text)] -> Bool+isObs xs =+ let hasObs cls = "obs" `elem` T.splitOn " " cls+ act = hasObs <$> lookup "class" xs+ in fromMaybe False act++ +-- Is this a row we care about? The choice is made by+-- only looking for rows with a class name of 'obs'.+--+extractRow :: [Tag T.Text] -> Maybe Row+extractRow (TagOpen "tr" attrs : ts) | isObs attrs = toRow ts+extractRow _ = Nothing+ ++-- extract the text contents of the td element; highly specialised+-- to this use case+getText :: [Tag T.Text] -> T.Text+getText (TagText txt:_) = txt+getText (_:ts) = getText ts+getText [] = error "unexpected data"+++-- probably not enough data checks+toRow :: [Tag T.Text] -> Maybe Row+toRow tgs =+ let tds = partitions (isTagOpenName "td") tgs+ jdTag = getText (tds !! 2)+ dateTag = getText (tds !! 3)+ magTag = getText (tds !! 4)+ magETag = getText (tds !! 5)+ filterTag = getText (tds !! 6)+ obsTag = getText (tds !! 7)++ magE = readMaybe (T.unpack magETag)++ in if length tds /= 9+ then Nothing+ else Row <$> readMaybe (T.unpack jdTag)+ <*> pure dateTag+ <*> readMaybe (T.unpack magTag)+ <*> pure magE+ <*> pure filterTag+ <*> pure obsTag+++processPage :: T.Text -> [Row]+processPage = extractRows . parseTags+++main :: IO ()+main = do+ rs <- mapM (fmap processPage . getPage) [1..nPages]++ -- assuming no chance of getting repeated rows from this query+ let rows = concat rs+ srows = sortBy (compare `on` jd) rows++ js = encode srows++ L8.putStrLn js+
tools/PlayTutorial.hs view
@@ -57,34 +57,110 @@ import System.FilePath ((</>), FilePath) import System.IO (hPutStrLn, stderr) +import Prelude hiding (filter)+ -- Could use TH to get at the tutorials, but for now hard code the names. -- The intro visualization used in the API documentation (not in -- the tutorial). ---vl1 :: VegaLite-vl1 =- let desc = "A very exciting bar chart"+-- How does Betelgeuse vary with time?+--+betelgeuse :: VegaLite+betelgeuse =+ let desc = "How has Betelgeuse's brightness varied, based on data collated by AAVSO (https://www.aavso.org/). You should also look at https://twitter.com/betelbot and https://github.com/hippke/betelbot. It was all the rage on social media at the start of 2020." - dat = dataFromRows [Parse [("start", FoDate "%Y-%m-%d")]]- . dataRow [("start", Str "2011-03-25"), ("count", Number 23)]- . dataRow [("start", Str "2011-04-02"), ("count", Number 45)]- . dataRow [("start", Str "2011-04-12"), ("count", Number 3)]+ titleStr = "Betelegeuse's magnitude measurements, collated by AAVSO" - barOpts = [MOpacity 0.4, MColor "teal"]+ -- height and width of individual plots+ w = width 600+ h = height 150 - enc = encoding- . position X [PName "start", PmType Temporal, PAxis [AxTitle "Inception date"]]- . position Y [PName "count", PmType Quantitative]+ pos1Opts fld ttl = [PName fld, PmType Quantitative, PAxis [AxTitle ttl]]+ x1Opts = pos1Opts "days" "Days since January 1, 2020"+ y1Opts = pos1Opts "magnitude" "Magnitude" ++ [PSort [Descending], yRange]+ yRange = PScale [SDomain (DNumbers [-1, 3])] - in toVegaLite [description desc, background "white"- , dat [], mark Bar barOpts, enc []]+ filtOpts = [MName "filterName", MmType Nominal]+ filtEnc = color (MLegend [ LTitle "Filter", LTitleFontSize 16, LLabelFontSize 14 ] : filtOpts)+ . shape filtOpts + circle = mark Point [ MOpacity 0.5, MFilled False ] + encOverview = encoding+ . position X x1Opts+ . position Y y1Opts+ . filtEnc++ selName = "brush"+ pos2Opts fld = [PName fld, PmType Quantitative, PAxis [AxNoTitle],+ PScale [SDomain (DSelectionField selName fld)]]+ x2Opts = pos2Opts "days"+ y2Opts = pos2Opts "magnitude" ++ [PSort [Descending]]++ encDetail = encoding+ . position X x2Opts+ . position Y y2Opts+ . filtEnc++ xlim = (Number (-220), Number 100)+ ylim = (Number (-0.5), Number 2.5)+ overview = asSpec [ w+ , h+ , encOverview []+ , selection+ . select selName Interval [ Encodings [ChX, ChY]+ , SInitInterval (Just xlim) (Just ylim)+ ]+ $ []+ , circle+ ]++ detailPlot = asSpec [ w+ , h+ , encDetail []+ , circle+ ]++ headerOpts = [ HLabelFontSize 16+ , HLabelAlign AlignRight+ , HLabelAnchor AEnd+ , HLabelPadding (-24)+ , HNoTitle+ , HLabelExpr "'Filter: ' + datum.label"+ ]++ details = asSpec [ columns 1+ , facetFlow [ FName "filterName"+ , FmType Nominal+ , FHeader headerOpts+ ]+ , spacing 10+ , specification detailPlot+ ]++ in toVegaLite [ description desc+ , title titleStr [ TFontSize 18 ]+ , dataFromUrl "https://raw.githubusercontent.com/DougBurke/hvega/master/hvega/data/betelgeuse-2020-03-19.json" []+ , transform+ -- concentrate on the two filters with a reasonable number of points+ . filter (FExpr "datum.filterName[0] === 'V'")+ -- remove some "outliers"+ . filter (FExpr "datum.magnitude < 4")+ -- subtract Jan 1 2020 (start of day, hence the .0 rather than .5)+ . calculateAs "datum.jd - 2458849.0" "days"+ $ []+ , vConcat [overview, details]+ , configure+ . configuration (Axis [ TitleFontWeight Normal, TitleFontSize 16, LabelFontSize 14 ])+ $ []+ ]++ -- Associate a name with each visualization -- vl :: [(String, VegaLite)]-vl = [ ("api-vl1", vl1)+vl = [ ("api-betelgeuse", betelgeuse) , ("stripplot", VL.stripPlot) , ("stripplotwithbackground", VL.stripPlotWithBackground) , ("stripploty", VL.stripPlotY)