packages feed

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 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 -[![vega-lite version](https://img.shields.io/badge/Vega--Lite-v4.5-purple.svg)](https://vega.github.io/vega-lite/)+[![vega-lite version](https://img.shields.io/badge/Vega--Lite-v4.7-purple.svg)](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  ![Simple scatterplot](https://raw.githubusercontent.com/DougBurke/hvega/master/hvega/images/intro.png "Simple scatterplot") +### 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:++![PNG version of the lightcurve](https://raw.githubusercontent.com/DougBurke/hvega/master/hvega/images/example.png "PNG version of the lightcurve")+ ## 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)