{-# 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 .  " " " 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

# LANGUAGE RankNTypes #
 | Module containing renderable objects and plots for our maps
 | Given a map, return a `Renderable ()` for use with the
 | Helper function to plot data points as appropriate for a map, i.e. using
 contiguous lines. 
 | Set labels via a lens field.
 | 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. 

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

  " " " Graphics . Rendering .  " " " 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 ]

plotDataPoints :: [Polygon] -> Plot Double Double
plotDataPoints points = toPlot $
    plot_lines_values .~ points $ plot_lines_style . line_color .~ opaque blue $ plot_lines_title .~ "Border" $ def

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

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
