Files
to-hen/onomastics-ng/lib/Onomap/Svg.hs

125 lines
6.0 KiB
Haskell
Raw Normal View History

2022-04-17 21:04:13 +02:00
{-# LANGUAGE OverloadedStrings #-}
2022-04-19 23:11:06 +02:00
module Onomap.Svg (drawMap, renderMap) where
2022-04-17 21:04:13 +02:00
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
2022-10-07 02:21:34 +02:00
import Data.Text.Lazy (toStrict)
2022-04-17 21:04:13 +02:00
import Graphics.Svg
2022-10-07 02:21:34 +02:00
import Onomap.Types (Area (..), ByArea (..), ScaleToMaximum (..), SvgSettings (..))
2022-04-17 21:04:13 +02:00
import Text.Printf (printf)
2022-10-07 02:21:34 +02:00
renderMap :: SvgSettings -> [Text] -> [Area] -> [(Text, ByArea Double)] -> Text
renderMap settings colorPalette areas statistics = toStrict $ prettyText $ drawMap settings colorPalette areas statistics
2022-04-19 23:11:06 +02:00
2022-10-07 02:21:34 +02:00
drawMap :: SvgSettings -> [Text] -> [Area] -> [(Text, ByArea Double)] -> Element
drawMap settings colorPalette areas statistics =
doctype <> with (svg11_ content) [Version_ <<- "1.1", Width_ <<- "650", Height_ <<- Text.pack (show (900 + 20 * length statistics))]
2022-04-17 21:04:13 +02:00
where
2022-10-07 02:21:34 +02:00
theMaxima = map maximum $ map (getByArea . snd) statistics
globalMaximum = maximum theMaxima
2022-04-17 21:04:13 +02:00
localize = Text.replace "." "," . Text.dropWhileEnd (== '.') . Text.dropWhileEnd (== '0')
showRounded = Text.pack . printf "%.2f"
2022-10-07 02:21:34 +02:00
frameColor = "black"
2022-04-17 21:04:13 +02:00
areaPaths =
foldMap
( \area ->
2022-10-07 02:21:34 +02:00
path_ [Stroke_ <<- frameColor, Stroke_width_ <<- "0.3px", Fill_ <<- "none", D_ <<- path area]
<> foldMap
( \((surname, statistic), color, theMaximum) ->
let count = fromMaybe 0 (Map.lookup (key area) (getByArea statistic))
theTitle =
( title_
[]
( toElement $
mconcat
[ name area
, ": "
, Text.intercalate
", "
( map
( \(surname, statistic) ->
let count = fromMaybe 0 (Map.lookup (key area) (getByArea statistic))
in localize (showRounded count) <> " (" <> Text.toTitle surname <> ")"
)
statistics
)
2022-10-07 02:21:34 +02:00
]
)
)
in path_
[ D_ <<- path area
, Fill_ <<- color
, Fill_opacity_
<<- showRounded
( if count == 0
then 0
else
count
/ ( case scaleToMaximum settings of
Global -> globalMaximum
Local -> theMaximum
)
)
, Stroke_ <<- "none"
]
theTitle
2022-04-17 21:04:13 +02:00
)
2022-10-07 02:21:34 +02:00
(zip3 statistics (cycle colorPalette) theMaxima)
2022-04-17 21:04:13 +02:00
)
areas
content =
2022-10-07 02:21:34 +02:00
foldMap
( \(index, color, theMaximum) ->
defs_
[]
( linearGradient_
[ Id_ <<- "legend" <> Text.pack (show index)
, X1_ <<- "0"
, X2_ <<- case scaleToMaximum settings of
Global -> Text.pack (show (recip $ theMaximum / globalMaximum))
Local -> "1"
, Y1_ <<- "0"
, Y2_ <<- "0"
2022-04-17 21:04:13 +02:00
]
2022-10-07 02:21:34 +02:00
( stop_ [Offset_ <<- "0%", Stop_color_ <<- "white"]
<> stop_ [Offset_ <<- "100%", Stop_color_ <<- color]
)
)
)
(zip3 [0 ..] colorPalette theMaxima)
<> foldMap
2022-10-09 07:14:11 +02:00
( \(index, (name, statistic)) ->
2022-10-07 02:21:34 +02:00
g_
[]
( rect_
[ X_ <<- "165"
, Y_ <<- Text.pack (show (880 + 20 * index))
, Stroke_ <<- frameColor
, Width_ <<- "350"
, Height_ <<- "10"
, Fill_ <<- "url(#legend" <> Text.pack (show index) <> ")"
]
<> text_
[ X_ <<- "155"
, Y_ <<- Text.pack (show (890 + 20 * index))
, Font_size_ <<- "18"
, Fill_ <<- "black"
, Text_anchor_ <<- "end"
]
(toElement $ Text.toTitle name)
<> text_
[ X_ <<- "525"
, Y_ <<- Text.pack (show (890 + 20 * index))
, Font_size_ <<- "18"
, Fill_ <<- "black"
]
(toElement $ localize $ showRounded $ maximum $ getByArea statistic)
)
2022-04-17 21:04:13 +02:00
)
2022-10-09 07:14:11 +02:00
(zip [0 ..] statistics)
2022-10-07 02:21:34 +02:00
<> style_ [] "path { mix-blend-mode: multiply; }"
2022-04-17 21:04:13 +02:00
<> g_ [] areaPaths