{-# LANGUAGE RankNTypes #-}

-- | Module containing renderable objects and plots for our maps
module GIS.Graphics.Plot where

import GIS.Types
import Graphics.Rendering.Chart
import Graphics.Rendering.Chart.Easy
import GIS.Math.Spherical
import GIS.Utils
import Data.Colour.Names
import GIS.Graphics.Types
import GIS.Math.Projections

-- | Given a map, return a `Renderable ()` for use with the
-- """Graphics.Rendering.Char""" module. 
mkMapR :: Map -> Renderable ()
mkMapR (Map { _projection = p , _title = tit , _labelEntities = le , _labelledDistricts = points }) = case le of
    True -> mkRenderableLabelled tit points
    False -> mkRenderableLabelled tit $ fmap (over _2 (const "")) points

mkRenderablePlots :: String -> [Plot Double Double] -> Renderable ()
mkRenderablePlots title plots = toRenderable $
    layout_title .~ title $ layout_plots .~ plots $ def

mkRenderableLens :: (Show a) => Lens' District a -> [District] -> String -> Renderable ()
mkRenderableLens lens districts title = mkRenderableLabelled title (labelByLens lens districts)

mkRenderableLabelled :: String -> [([Polygon], String)] -> Renderable ()
mkRenderableLabelled title points = mkRenderablePlots title [ plotDataPoints (concat . (fmap fst) $ points), plotLabels points ]

-- | Helper function to plot data points as appropriate for a map, i.e. using
-- contiguous lines. 
plotDataPoints :: [Polygon] -> Plot Double Double
plotDataPoints points = toPlot $
    plot_lines_values .~ points $ plot_lines_style . line_color .~ opaque blue $ plot_lines_title .~ "Border" $ def

-- | Set labels via a lens field.
labelByLens :: (Show a) => Lens' District a -> [District] -> [([Polygon], String)]
labelByLens lens districts = zip (fmap (view shape) districts) (fmap (show . (view lens)) districts)

-- | Helper function to plot labels on a map. The ordinate will be plotted at
-- the centroid of the abscissa, which may be outside the polygon if it is
-- concave. 
plotLabels :: [([Polygon], String)] -> Plot Double Double
plotLabels points = toPlot texts
    where pairs = fmap (flatten . (over _1 (shittyCentroid . concat))) points
          fontStyle = font_size .~ 15 $ font_weight .~ FontWeightBold $ def
          texts = plot_annotation_values .~ pairs $ plot_annotation_style .~ fontStyle $ def