{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : Graphics.Vega.VegaLite.Time Copyright : (c) Douglas Burke, 2018-2020 License : BSD3 Maintainer : dburke.gw@gmail.com Stability : unstable Portability : CPP, OverloadedStrings Time-related types. -} module Graphics.Vega.VegaLite.Time ( DateTime(..) , MonthName(..) , DayName(..) , TimeUnit(..) -- not for external export , dateTimeProperty , timeUnitSpec ) where 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) import Graphics.Vega.VegaLite.Specification (LabelledSpec, VLSpec) {-| Allows a date or time to be represented. This is typically part of a list of @DateTime@ items to provide a specific point in time. For details see the <https://vega.github.io/vega-lite/docs/types.html#datetime Vega-Lite documentation>. There is __no check__ that the provided @Int@ values lie within the required bounds. A 'DateTime' value of 'DTDay' or 'DTDayNum' should not be combined with 'DTYear', 'DTQuarter', 'DTMonth', 'DTMonthNum', or 'DTDate'. -} data DateTime = DTYear Int | DTQuarter Int -- ^ The quarter of the year (1 to 4, inclusive). | DTMonth MonthName | DTMonthNum Int -- ^ The month number (1 to 12, inclusive). -- -- @since 0.5.0.0 | DTDate Int -- ^ Day of the month (1 to 31, inclusive). | DTDay DayName | DTDayNum Int -- ^ The day number (1 represents Monday, 7 is Sunday). -- -- @since 0.5.0.0 | DTHours Int -- ^ Hour of the day, where 0 is midnight, 1 is 1am, and -- 23 is 11pm. | DTMinutes Int -- ^ The minute of an hour (0 to 59, inclusive). | DTSeconds Int -- ^ The second of a minute (0 to 59, inclusive). | DTMilliseconds Int -- ^ The milliseconds of a second (0 to 999, inclusive). -- | Identifies the day of the week. data DayName = Mon | Tue | Wed | Thu | Fri | Sat | Sun -- | Identifies a month of the year. data MonthName = Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec {-| Describes a unit of time. Useful for encoding and transformations. See the <https://vega.github.io/vega-lite/docs/timeunit.html Vega-Lite documentation> for further details. @ 'Graphics.Vega.VegaLite.encoding' . 'Graphics.Vega.VegaLite.position' 'Graphics.Vega.VegaLite.X' [ 'Graphics.Vega.VegaLite.PName' "date", 'Graphics.Vega.VegaLite.PmType' 'Graphics.Vega.VegaLite.Temporal', 'Graphics.Vega.VegaLite.PTimeUnit' ('Utc' 'YearMonthDateHours') ] @ -} -- Vega-Lite 4.4.0 has -- LocalMultiTimeUnit which is yearquarter, yearquartermonth, ,secondsmilliseconds -- LocalSingleTimeUnit year, quarter, ..., milliseconds -- and -- UtcMultiTimeUnit which is utc <> LocalMultiTimeUnit -- UtcSingleTimeUnit utc <> LocalSingleTimeUnit -- -- TimeUnit is either of SingleTimeUnit or MultiTimeUnit -- SingleTimeUnut is either of LocalSingleTimeUnit or UtcSingleTimeUnit -- MultiTimeUnit is either of LocalMultiTieUnit or UtcMultiTimeUnit -- -- "timeUnit" settings are TimeUnit or TimeUnitParams -- -- TimeUnitParams is an object with fields -- maxbins - number -- step - number -- unit - this is TimeUnit -- utc - boolean -- -- So, could be something like "TU <time unit type> [options]" -- where an empty array means it's a "TimeUnit", and the options are from -- TimeUnitParams (apart from the unit field). Unfortunately this doesn't -- capture the use case of supplying "maxbins" only (it may be that "step" -- can also be used without any other value). -- data TimeUnit = Year -- ^ Year. | YearQuarter -- ^ Year and quarter. | YearQuarterMonth -- ^ Year, quarter, and month. | YearMonth -- ^ Year and month. | YearMonthDate -- ^ Year, month, and day of month. | YearMonthDateHours -- ^ Year, month, day of month, and hour of day. | YearMonthDateHoursMinutes -- ^ Year, month, day of month, hour of day, and minutes. | YearMonthDateHoursMinutesSeconds -- ^ Year, month, day of month, hour of day, minutes, and seconds. | Quarter -- ^ Quarter of the year. | QuarterMonth -- ^ Quarter of the year and month. | Month -- ^ Month of the year. | MonthDate -- ^ Month of the year and day of the month. | Date -- ^ Day of the month (1 to 31). | Day -- ^ Day of the week. | Hours -- ^ Hour of the day. | HoursMinutes -- ^ Hour of the day and minutes. | HoursMinutesSeconds -- ^ Hour of the day, minutes, and seconds. | Minutes -- ^ Minutes of the hour. | MinutesSeconds -- ^ Minutes of the hour and seconds. | Seconds -- ^ Seconds of the minute. | SecondsMilliseconds -- ^ Seconds of the minute and milliseconds. | Milliseconds -- ^ Milliseconds. | Utc TimeUnit -- ^ Encode a time as UTC (coordinated universal time, independent of local time -- zones or daylight saving). | TUMaxBins Natural -- ^ The maximum number of bins to use when discretising time values. -- This can be useful as an algternative to explicitly providing the -- time unit to bin by, as it will be inferred from the temporal -- extent and the number of bins. As an example, @TUMaxBins 366@ -- will bin by day when applied to a dataset of hourly readings -- for a full year. -- -- @since 0.6.0.0 | TUStep Double TimeUnit -- ^ The number of steps between time-unit bins, in terms of the -- least-significant unit provided. So @TUStep 14 YearMonthDate@ -- wull bin temporal data into bi-weekly groups. -- -- @since 0.6.0.0 dateTimeProperty :: DateTime -> LabelledSpec dateTimeProperty (DTYear y) = "year" .= y dateTimeProperty (DTQuarter q) = "quarter" .= q dateTimeProperty (DTMonth mon) = "month" .= monthNameLabel mon dateTimeProperty (DTMonthNum n) = "month" .= n dateTimeProperty (DTDate dt) = "date" .= dt dateTimeProperty (DTDay day) = "day" .= dayLabel day dateTimeProperty (DTDayNum n) = "day" .= n dateTimeProperty (DTHours h) = "hours" .= h dateTimeProperty (DTMinutes m) = "minutes" .= m dateTimeProperty (DTSeconds s) = "seconds" .= s dateTimeProperty (DTMilliseconds ms) = "milliseconds" .= ms dayLabel :: DayName -> T.Text dayLabel Mon = "Mon" dayLabel Tue = "Tue" dayLabel Wed = "Wed" dayLabel Thu = "Thu" dayLabel Fri = "Fri" dayLabel Sat = "Sat" dayLabel Sun = "Sun" monthNameLabel :: MonthName -> T.Text monthNameLabel Jan = "Jan" monthNameLabel Feb = "Feb" monthNameLabel Mar = "Mar" monthNameLabel Apr = "Apr" monthNameLabel May = "May" monthNameLabel Jun = "Jun" monthNameLabel Jul = "Jul" monthNameLabel Aug = "Aug" monthNameLabel Sep = "Sep" monthNameLabel Oct = "Oct" monthNameLabel Nov = "Nov" monthNameLabel Dec = "Dec" -- Assume there's no "embedded" values the time unit used by -- the "grouping" cases, such as Utc, are "singular" and not -- themselves compound. -- -- Ideally this would know when it could just return the label -- and not a labelled spec, but for now leave it as is. -- timeHelper :: T.Text -> [LabelledSpec] timeHelper unit = ["unit" .= unit] timeUnitProperties :: TimeUnit -> [LabelledSpec] timeUnitProperties Year = timeHelper "year" timeUnitProperties YearQuarter = timeHelper "yearquarter" timeUnitProperties YearQuarterMonth = timeHelper "yearquartermonth" timeUnitProperties YearMonth = timeHelper "yearmonth" timeUnitProperties YearMonthDate = timeHelper "yearmonthdate" timeUnitProperties YearMonthDateHours = timeHelper "yearmonthdatehours" timeUnitProperties YearMonthDateHoursMinutes = timeHelper "yearmonthdatehoursminutes" timeUnitProperties YearMonthDateHoursMinutesSeconds = timeHelper "yearmonthdatehoursminutesseconds" timeUnitProperties Quarter = timeHelper "quarter" timeUnitProperties QuarterMonth = timeHelper "quartermonth" timeUnitProperties Month = timeHelper "month" timeUnitProperties MonthDate = timeHelper "monthdate" timeUnitProperties Date = timeHelper "date" timeUnitProperties Day = timeHelper "day" timeUnitProperties Hours = timeHelper "hours" timeUnitProperties HoursMinutes = timeHelper "hoursminutes" timeUnitProperties HoursMinutesSeconds = timeHelper "hoursminutesseconds" timeUnitProperties Minutes = timeHelper "minutes" timeUnitProperties MinutesSeconds = timeHelper "minutesseconds" timeUnitProperties Seconds = timeHelper "seconds" timeUnitProperties SecondsMilliseconds = timeHelper "secondsmilliseconds" timeUnitProperties Milliseconds = timeHelper "milliseconds" timeUnitProperties (Utc tu) = "utc" .= True : timeUnitProperties tu timeUnitProperties (TUStep x tu) = "step" .= x : timeUnitProperties tu timeUnitProperties (TUMaxBins n) = [ "maxbins" .= n ] -- Special case this so that -- {'unit': blah} -> blah -- {'unit': blah, 'utc': true} -> 'utc' <> blah [would be nice but not done for now] -- timeUnitSpec :: TimeUnit -> VLSpec timeUnitSpec tu = let props = timeUnitProperties tu in case props of [(k, v)] | k == "unit" -> v _ -> object props