Skip to content

Instantly share code, notes, and snippets.

@rhwlo
Created May 29, 2016 22:09
Show Gist options
  • Save rhwlo/b6587c42ea2b925eafdd6ca0a1fe5bdf to your computer and use it in GitHub Desktop.
Save rhwlo/b6587c42ea2b925eafdd6ca0a1fe5bdf to your computer and use it in GitHub Desktop.
An Elm program to visualize BART routes
{- this is hosted currently at http://fearchar.net/2016/May/29/bart-elm-routes -}
import Debug exposing (log)
import Dict exposing (Dict)
import Json.Decode as Json exposing ((:=))
import Maybe
import Result
import String
import Html.App as Html
import Html exposing (Html)
import Svg exposing (..)
import Svg.Attributes as SA exposing (..)
import Svg.Events exposing (onMouseOver, onMouseOut)
import Char exposing (isHexDigit, toLocaleLower, toCode)
type alias RGB = (Int, Int, Int)
type alias Route = { name : String, abbreviation : String, id : String, number : Int, color: String, stationCount : Int, stations : List String }
type alias Station = { abbreviation: String, name : String, latitude : Float, longitude : Float }
type alias Model = (List Route, Dict String Station, Maybe Route)
type Msg = Highlight Route | Unhighlight
sourceJson : String
sourceJson = """{"routes":[{"Name":"Pittsburg/Bay Point - SFIA/Millbrae","Abbreviation":"PITT-SFIA","ID":"ROUTE 1","Number":1,"Color":"#ffff33","Holidays":1,"StationCount":26,"Stations":[{"Abbreviation":"PITT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"NCON","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CONC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"PHIL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WCRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAFY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ORIN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ROCK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MCAR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"19TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"12TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLM","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SSAN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SBRN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SFIA","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MLBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Millbrae/SFIA - Pittsburg/Bay Point","Abbreviation":"SFIA-PITT","ID":"ROUTE 2","Number":2,"Color":"#ffff33","Holidays":1,"StationCount":26,"Stations":[{"Abbreviation":"MLBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SFIA","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SBRN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SSAN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLM","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"12TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"19TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MCAR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ROCK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ORIN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAFY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WCRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"PHIL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CONC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"NCON","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"PITT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Fremont - Richmond","Abbreviation":"FRMT-RICH","ID":"ROUTE 3","Number":3,"Color":"#ff9933","Holidays":1,"StationCount":18,"Stations":[{"Abbreviation":"FRMT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"UCTY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SHAY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"HAYW","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BAYF","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SANL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FTVL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAKE","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"12TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"19TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MCAR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ASHB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"NBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"PLZA","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DELN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"RICH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Richmond - Fremont","Abbreviation":"RICH-FRMT","ID":"ROUTE 4","Number":4,"Color":"#ff9933","Holidays":1,"StationCount":18,"Stations":[{"Abbreviation":"RICH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DELN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"PLZA","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"NBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ASHB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MCAR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"19TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"12TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAKE","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FTVL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SANL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BAYF","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"HAYW","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SHAY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"UCTY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FRMT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Fremont - Daly City","Abbreviation":"FRMT-DALY","ID":"ROUTE 5","Number":5,"Color":"#339933","Holidays":0,"StationCount":19,"Stations":[{"Abbreviation":"FRMT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"UCTY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SHAY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"HAYW","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BAYF","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SANL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FTVL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAKE","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Daly City - Fremont","Abbreviation":"DALY-FRMT","ID":"ROUTE 6","Number":6,"Color":"#339933","Holidays":0,"StationCount":19,"Stations":[{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAKE","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FTVL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SANL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BAYF","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"HAYW","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SHAY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"UCTY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FRMT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Richmond - Daly City/Millbrae","Abbreviation":"RICH-MLBR","ID":"ROUTE 7","Number":7,"Color":"#ff0000","Holidays":0,"StationCount":23,"Stations":[{"Abbreviation":"RICH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DELN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"PLZA","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"NBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ASHB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MCAR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"19TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"12TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLM","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SSAN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SBRN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MLBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Millbrae/Daly City - Richmond","Abbreviation":"MLBR-RICH","ID":"ROUTE 8","Number":8,"Color":"#ff0000","Holidays":0,"StationCount":23,"Stations":[{"Abbreviation":"MLBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SBRN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SSAN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLM","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"12TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"19TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MCAR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"ASHB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"NBRK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"PLZA","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DELN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"RICH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Dublin/Pleasanton - Daly City","Abbreviation":"DUBL-DALY","ID":"ROUTE 11","Number":11,"Color":"#0099cc","Holidays":1,"StationCount":17,"Stations":[{"Abbreviation":"DUBL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WDUB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CAST","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BAYF","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SANL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FTVL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAKE","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Daly City - Dublin/Pleasanton","Abbreviation":"DALY-DUBL","ID":"ROUTE 12","Number":12,"Color":"#0099cc","Holidays":1,"StationCount":17,"Stations":[{"Abbreviation":"DALY","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BALB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"GLEN","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"24TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"16TH","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CIVC","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"POWL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"MONT","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"EMBR","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WOAK","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"LAKE","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"FTVL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"SANL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"BAYF","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"CAST","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"WDUB","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"DUBL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Coliseum - Oakland Int'l Airport","Abbreviation":"COLS-OAKL","ID":"ROUTE 19","Number":19,"Color":"#d5cfa3","Holidays":1,"StationCount":2,"Stations":[{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"OAKL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]},{"Name":"Oakland Int'l Airport - Coliseum","Abbreviation":"OAKL-COLS","ID":"ROUTE 20","Number":20,"Color":"#d5cfa3","Holidays":1,"StationCount":2,"Stations":[{"Abbreviation":"OAKL","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""},{"Abbreviation":"COLS","Name":"","Latitude":0,"Longitude":0,"Address":"","City":"","County":"","State":"","ZIPCode":""}]}],
"stations":[{"Abbreviation":"12TH","Name":"12th St. Oakland City Center","Latitude":37.803665,"Longitude":-122.27161,"Address":"1245 Broadway","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94612"},{"Abbreviation":"16TH","Name":"16th St. Mission","Latitude":37.76506,"Longitude":-122.41969,"Address":"2000 Mission Street","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94110"},{"Abbreviation":"19TH","Name":"19th St. Oakland","Latitude":37.80787,"Longitude":-122.26903,"Address":"1900 Broadway","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94612"},{"Abbreviation":"24TH","Name":"24th St. Mission","Latitude":37.752254,"Longitude":-122.418465,"Address":"2800 Mission Street","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94110"},{"Abbreviation":"ASHB","Name":"Ashby","Latitude":37.853024,"Longitude":-122.26978,"Address":"3100 Adeline Street","City":"Berkeley","County":"alameda","State":"CA","ZIPCode":"94703"},{"Abbreviation":"BALB","Name":"Balboa Park","Latitude":37.72198,"Longitude":-122.44741,"Address":"401 Geneva Avenue","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94112"},{"Abbreviation":"BAYF","Name":"Bay Fair","Latitude":37.697186,"Longitude":-122.12687,"Address":"15242 Hesperian Blvd.","City":"San Leandro","County":"alameda","State":"CA","ZIPCode":"94578"},{"Abbreviation":"CAST","Name":"Castro Valley","Latitude":37.690754,"Longitude":-122.07557,"Address":"3301 Norbridge Dr.","City":"Castro Valley","County":"alameda","State":"CA","ZIPCode":"94546"},{"Abbreviation":"CIVC","Name":"Civic Center/UN Plaza","Latitude":37.77953,"Longitude":-122.41376,"Address":"1150 Market Street","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94102"},{"Abbreviation":"COLS","Name":"Coliseum","Latitude":37.754005,"Longitude":-122.19727,"Address":"7200 San Leandro St.","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94621"},{"Abbreviation":"COLM","Name":"Colma","Latitude":37.68464,"Longitude":-122.46623,"Address":"365 D Street","City":"Colma","County":"sanmateo","State":"CA","ZIPCode":"94014"},{"Abbreviation":"CONC","Name":"Concord","Latitude":37.973736,"Longitude":-122.0291,"Address":"1451 Oakland Avenue","City":"Concord","County":"contracosta","State":"CA","ZIPCode":"94520"},{"Abbreviation":"DALY","Name":"Daly City","Latitude":37.70612,"Longitude":-122.46908,"Address":"500 John Daly Blvd.","City":"Daly City","County":"sanmateo","State":"CA","ZIPCode":"94014"},{"Abbreviation":"DBRK","Name":"Downtown Berkeley","Latitude":37.869865,"Longitude":-122.26804,"Address":"2160 Shattuck Avenue","City":"Berkeley","County":"alameda","State":"CA","ZIPCode":"94704"},{"Abbreviation":"DUBL","Name":"Dublin/Pleasanton","Latitude":37.701694,"Longitude":-121.90037,"Address":"5801 Owens Dr.","City":"Pleasanton","County":"alameda","State":"CA","ZIPCode":"94588"},{"Abbreviation":"DELN","Name":"El Cerrito del Norte","Latitude":37.925655,"Longitude":-122.31727,"Address":"6400 Cutting Blvd.","City":"El Cerrito","County":"contracosta","State":"CA","ZIPCode":"94530"},{"Abbreviation":"PLZA","Name":"El Cerrito Plaza","Latitude":37.903057,"Longitude":-122.29927,"Address":"6699 Fairmount Avenue","City":"El Cerrito","County":"contracosta","State":"CA","ZIPCode":"94530"},{"Abbreviation":"EMBR","Name":"Embarcadero","Latitude":37.792976,"Longitude":-122.39674,"Address":"298 Market Street","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94111"},{"Abbreviation":"FRMT","Name":"Fremont","Latitude":37.557354,"Longitude":-121.9764,"Address":"2000 BART Way","City":"Fremont","County":"alameda","State":"CA","ZIPCode":"94536"},{"Abbreviation":"FTVL","Name":"Fruitvale","Latitude":37.774963,"Longitude":-122.22427,"Address":"3401 East 12th Street","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94601"},{"Abbreviation":"GLEN","Name":"Glen Park","Latitude":37.73292,"Longitude":-122.43409,"Address":"2901 Diamond Street","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94131"},{"Abbreviation":"HAYW","Name":"Hayward","Latitude":37.6704,"Longitude":-122.08797,"Address":"699 'B' Street","City":"Hayward","County":"alameda","State":"CA","ZIPCode":"94541"},{"Abbreviation":"LAFY","Name":"Lafayette","Latitude":37.893394,"Longitude":-122.1238,"Address":"3601 Deer Hill Road","City":"Lafayette","County":"contracosta","State":"CA","ZIPCode":"94549"},{"Abbreviation":"LAKE","Name":"Lake Merritt","Latitude":37.797485,"Longitude":-122.26561,"Address":"800 Madison Street","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94607"},{"Abbreviation":"MCAR","Name":"MacArthur","Latitude":37.828415,"Longitude":-122.26723,"Address":"555 40th Street","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94609"},{"Abbreviation":"MLBR","Name":"Millbrae","Latitude":37.59979,"Longitude":-122.38666,"Address":"200 North Rollins Road","City":"Millbrae","County":"sanmateo","State":"CA","ZIPCode":"94030"},{"Abbreviation":"MONT","Name":"Montgomery St.","Latitude":37.789257,"Longitude":-122.401405,"Address":"598 Market Street","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94104"},{"Abbreviation":"NBRK","Name":"North Berkeley","Latitude":37.87404,"Longitude":-122.28345,"Address":"1750 Sacramento Street","City":"Berkeley","County":"alameda","State":"CA","ZIPCode":"94702"},{"Abbreviation":"NCON","Name":"North Concord/Martinez","Latitude":38.003277,"Longitude":-122.0246,"Address":"3700 Port Chicago Highway","City":"Concord","County":"contracosta","State":"CA","ZIPCode":"94520"},{"Abbreviation":"OAKL","Name":"Oakland Int'l Airport","Latitude":37.71297,"Longitude":-122.21244,"Address":"4 Airport Drive","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94621"},{"Abbreviation":"ORIN","Name":"Orinda","Latitude":37.87836,"Longitude":-122.18379,"Address":"11 Camino Pablo","City":"Orinda","County":"contracosta","State":"CA","ZIPCode":"94563"},{"Abbreviation":"PITT","Name":"Pittsburg/Bay Point","Latitude":38.018913,"Longitude":-121.94515,"Address":"1700 West Leland Road","City":"Pittsburg","County":"contracosta","State":"CA","ZIPCode":"94565"},{"Abbreviation":"PHIL","Name":"Pleasant Hill/Contra Costa Centre","Latitude":37.928402,"Longitude":-122.056015,"Address":"1365 Treat Blvd.","City":"Walnut Creek","County":"contracosta","State":"CA","ZIPCode":"94597"},{"Abbreviation":"POWL","Name":"Powell St.","Latitude":37.784992,"Longitude":-122.40686,"Address":"899 Market Street","City":"San Francisco","County":"sanfrancisco","State":"CA","ZIPCode":"94102"},{"Abbreviation":"RICH","Name":"Richmond","Latitude":37.936886,"Longitude":-122.353165,"Address":"1700 Nevin Avenue","City":"Richmond","County":"contracosta","State":"CA","ZIPCode":"94801"},{"Abbreviation":"ROCK","Name":"Rockridge","Latitude":37.8446,"Longitude":-122.25179,"Address":"5660 College Avenue","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94618"},{"Abbreviation":"SBRN","Name":"San Bruno","Latitude":37.637753,"Longitude":-122.41604,"Address":"1151 Huntington Avenue","City":"San Bruno","County":"sanmateo","State":"CA","ZIPCode":"94066"},{"Abbreviation":"SFIA","Name":"San Francisco Int'l Airport","Latitude":37.616035,"Longitude":-122.39261,"Address":"International Terminal, Level 3","City":"San Francisco Int'l Airport","County":"sanmateo","State":"CA","ZIPCode":"94128"},{"Abbreviation":"SANL","Name":"San Leandro","Latitude":37.72262,"Longitude":-122.16131,"Address":"1401 San Leandro Blvd.","City":"San Leandro","County":"alameda","State":"CA","ZIPCode":"94577"},{"Abbreviation":"SHAY","Name":"South Hayward","Latitude":37.6348,"Longitude":-122.05755,"Address":"28601 Dixon Street","City":"Hayward","County":"alameda","State":"CA","ZIPCode":"94544"},{"Abbreviation":"SSAN","Name":"South San Francisco","Latitude":37.664173,"Longitude":-122.444115,"Address":"1333 Mission Road","City":"South San Francisco","County":"sanmateo","State":"CA","ZIPCode":"94080"},{"Abbreviation":"UCTY","Name":"Union City","Latitude":37.59121,"Longitude":-122.01787,"Address":"10 Union Square","City":"Union City","County":"alameda","State":"CA","ZIPCode":"94587"},{"Abbreviation":"WCRK","Name":"Walnut Creek","Latitude":37.90563,"Longitude":-122.06742,"Address":"200 Ygnacio Valley Road","City":"Walnut Creek","County":"contracosta","State":"CA","ZIPCode":"94596"},{"Abbreviation":"WDUB","Name":"West Dublin/Pleasanton","Latitude":37.69976,"Longitude":-121.9281,"Address":"6501 Golden Gate Drive","City":"Dublin","County":"alameda","State":"CA","ZIPCode":"94568"},{"Abbreviation":"WOAK","Name":"West Oakland","Latitude":37.804676,"Longitude":-122.29458,"Address":"1451 7th Street","City":"Oakland","County":"alameda","State":"CA","ZIPCode":"94607"}]}"""
routesDecoder : Json.Decoder (List Route)
routesDecoder = let
idSanitize : String -> String
idSanitize = String.map <|
\c -> case c of
' ' -> '-'
_ -> c
in Json.at ["routes"] <| Json.list
<| Json.object7 Route
("Name" := Json.string)
("Abbreviation" := Json.string)
(Json.map idSanitize ("ID" := Json.string))
("Number" := Json.int)
("Color" := Json.string)
("StationCount" := Json.int)
("Stations" := (Json.list <| "Abbreviation" := Json.string))
stationsDecoder : Json.Decoder (Dict String Station)
stationsDecoder = Json.map Dict.fromList
<| Json.map (List.map (\s -> (s.abbreviation, s)))
<| Json.at ["stations"] <| Json.list
<| Json.object4 Station
("Abbreviation" := Json.string)
("Name" := Json.string)
("Latitude" := Json.float)
("Longitude" := Json.float)
staleRoutes : List Route
staleRoutes = Result.withDefault [] <| Json.decodeString routesDecoder sourceJson
staleStations : Dict String Station
staleStations = Result.withDefault Dict.empty <| Json.decodeString stationsDecoder sourceJson
init : Model
init = (staleRoutes, staleStations, Nothing)
main = Html.beginnerProgram {
model = init,
view = view,
update = update }
view : Model -> Svg Msg
view (routes, stations, maybeHighlight) = svg
[ version "1.1", x "0", y "0", width "1000", height "1000", viewBox "0 0 1000 1000" ]
((List.map makeRouteCircle routes) ++ (makeSvgMap routes stations maybeHighlight))
makeRouteCircle : Route -> Svg Msg
makeRouteCircle route = let
cxVal = 50 * ((route.number - 1) % 6 + 1)
cyVal = 50 * ((route.number - 1) // 6 + 1)
textX = cxVal - (6 * (String.length (toString route.number)))
textY = cyVal + 5
in g [id route.id] [circle [ fill route.color, stroke (complementS route.color), id route.id, cx (toString cxVal), cy (toString cyVal), r "20", SA.title route.name, onMouseOver (Highlight route), onMouseOut Unhighlight ] [], text' [stroke (complementS route.color), fill (complementS route.color), fontFamily "Verdana", fontSize "20px", x (toString textX), y (toString textY), SA.title route.name, onMouseOver (Highlight route), onMouseOut Unhighlight] [text (toString route.number)]]
makeSvgMap : List Route -> Dict String Station -> Maybe Route -> List (Svg Msg)
makeSvgMap troutes tstations maybeRoute = let
mapW = 600
mapH = 800
minLat = 37.557
maxLat = 38.019
minLong = -122.47
maxLong = -121.90
latScale = (mapH - 50) / (maxLat - minLat)
lonScale = (mapW - 50) / (maxLong - minLong)
drawStation : (String, Station) -> Svg Msg
drawStation (abbr, station) = let
stationColor : String
stationColor = Maybe.withDefault "#ffffff" (maybeRoute `Maybe.andThen` (\r ->
if List.member abbr r.stations then (Just r.color) else Nothing))
in circle [ fill stationColor, cx (toString <| (station.longitude - minLong) * lonScale + 385), cy (toString <| (maxLat - station.latitude) * latScale + 50), r "5", id ("stn-" ++ abbr)] []
in [ rect [x "360", y "25", fill "#000000", stroke "none", width "600", height "800"] [] ] ++ List.map drawStation (Dict.toList tstations)
complementS : String -> String
complementS color = let
rgb : RGB
rgb = Maybe.withDefault (0,0,0) <| rgbFromHex color
brightest : RGB -> Int
brightest (r, g, b) = List.foldl Basics.max 0 [r, g, b]
rgbToString : RGB -> String
rgbToString (r, g, b) = "rgb(" ++ toString r ++ "," ++ toString g ++ "," ++ toString b ++ ")"
complement : RGB
complement = let br = brightest rgb
in (255 - brightest rgb, 255 - brightest rgb, 255 - brightest rgb)
in rgbToString complement
rgbFromHex : String -> Maybe RGB
rgbFromHex h = let
fromHex : List Char -> Maybe Int
fromHex cs = let
fromHexC c me = let
mi = if not (isHexDigit c) then Nothing else case toLocaleLower c of
'a' -> Just 10
'b' -> Just 11
'c' -> Just 12
'd' -> Just 13
'e' -> Just 14
'f' -> Just 15
_ -> Just (toCode c - 48)
in Maybe.map2 (\e i -> e * 16 + i) me mi
in List.foldl fromHexC (Just 0) cs
maybeTake : Int -> List a -> Maybe (List a)
maybeTake n xs = if List.length (List.take n xs) < n then Nothing else Just (List.take n xs)
withoutHash : List Char
withoutHash = if String.left 1 h == "#" then List.drop 1 (String.toList h) else String.toList h
maybeColor : Int -> Maybe Int
maybeColor offset = (maybeTake 2 <| List.drop offset withoutHash) `Maybe.andThen` fromHex
red : Maybe Int
red = maybeColor 0
blue : Maybe Int
blue = maybeColor 2
green : Maybe Int
green = maybeColor 4
in
Maybe.map3 (,,) red blue green
update : Msg -> Model -> Model
update msg (routes, stations, _) = case msg of
Highlight r -> (routes, stations, Just r)
Unhighlight -> (routes, stations, Nothing)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment