Fsharp.Gdal


Plot Geometries

This section defines a rudimentary plot function to give at least some sort of graphical visualization of geometries.

To do this it makes use of FSharp.Charting's charts to plot the coordinates of geometries in a cartesian plan. The main limit of the function is that polygons are rendered as lines instead of full shapes.

1: 
2: 
open FSharp.Charting
open OSGeo

The coordinates function just extracts a list of tuples made of the longitude and latitude of the corrdinates that consistute the geometry.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
/// Extracts the geometry's coordinates as a list of tuples made of 
/// longitude and latitude.
let coordinates (geom:OGR.Geometry) = 
    let last = geom.GetPointCount() - 1
    [
        for i in 0..last ->
            let p = [|0.;0.|]
            geom.GetPoint(i,p)
            (p.[0], p.[1])
    ]

We need to set the space of the chart at a size that makes the shape visibile enough.

First with env we extract the bounding box of the geometry as an OGR.Envelope

1: 
2: 
3: 
4: 
5: 
/// Extracs the bounding box of the geometry
let env (geom:OGR.Geometry) = 
    let res = new OGR.Envelope()
    geom.GetEnvelope(res)
    res

... then we resize it based on a choosen zoom as a percentage of the shape size. If the geometry is made of just a point it has not a real size so in this case we just calculate a margin based on the coordinate's magnitude.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
/// Resizes the envelope based on a choosen zoom 
let resize zoom (en:OGR.Envelope) = 
    let dx = en.MaxX - en.MinX
    let dy = en.MaxY - en.MinY
    let xMargin = if dx = 0. then en.MaxX / zoom / 2. else ((dx / zoom) - dx) / 2.
    let yMargin = if dy = 0. then en.MaxY / zoom / 2. else ((dy / zoom) - dy) / 2.
    en.MaxX <- en.MaxX + xMargin
    en.MaxY <- en.MaxY + yMargin
    en.MinX <- en.MinX - xMargin
    en.MinY <- en.MinY - yMargin
    en

To properly draw the shapes in the cartesian space we need to choose a chart type that most fits with the geometry type to plot.

createChart creates a Chart.Point for points geometries while Chart.Line is choosen for every other geometry types: as marked above the main limit of this approach is that ploygons are still rendered as lines but this at least gives an immage of the geometry.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
/// Creates a chart for the geometry
let createChart (geom:OGR.Geometry) = 
    let coords = geom |> coordinates
    match (geom.GetGeometryType()) with
    | OGR.wkbGeometryType.wkbPoint
    | OGR.wkbGeometryType.wkbPoint25D   -> Chart.Point(geom |> coordinates)
    | _                                 -> Chart.Line(geom |> coordinates)

Now we can define a plotAt function that plots the geometry at a specified zoom.

Geomteries can be simple or compund. If geomis compound we need to recursively traverse its structure till its simple components and populate a charts list for each using the createChart function defined above.

The recursive part is made by the createCharts function taking advantage of the functional nature of F#.

At the end we can Chart.Combine all the elements of the chart list setting the cartesian plan's space at the proper size.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
let rec createCharts xs (geom:OGR.Geometry) = 
    let count = geom.GetGeometryCount()
    match count with
    | count when count = 0 -> [(geom |> createChart)]@xs
    | _ -> 
        [for i in 0..(count-1) -> 
            (geom.GetGeometryRef(i)) |> createCharts xs
        ] |> List.concat

/// Plots a geometry at a specified zoom
let plotAt zoom (geom:OGR.Geometry) = 
    let charts = geom |> createCharts []
    let spaceSize = geom |> env |> resize zoom
    Chart.Combine(charts)
        .WithXAxis(Max=spaceSize.MaxX,Min=spaceSize.MinX, Enabled = false)
        .WithYAxis(Max=spaceSize.MaxY,Min=spaceSize.MinY, Enabled = false)

Since normally a zoom at 80% of the shape size is enough to visualize the geometry we define the final plot function to default at a 80% zoom not to have to specify it each time:

1: 
2: 
/// Plots a geometry at a zoom of 80%
let plot = (fun g -> g |> plotAt 0.8)

Finally a function to bypass the lack of a shape chart and fill polygons' areas with lines

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
let fill (geom:OGR.Geometry) = 
    let geomEnv = geom |> env
    let xi = (geomEnv.MaxX - geomEnv.MinX) / 10.
    let lines = 
        [
            for i in [1.0..9.] -> 
                let x =  geomEnv.MinX + xi * i
                let line = new OGR.Geometry(OGR.wkbGeometryType.wkbLineString)
                line.AddPoint(x, geomEnv.MinY, 0.)
                line.AddPoint(x, geomEnv.MaxY, 0.)
                let intersection = line.Intersection(geom)
                intersection
        ]
    let geomcol = new OGR.Geometry(OGR.wkbGeometryType.wkbGeometryCollection)
    for l in lines do 
        geomcol.AddGeometry(l) |> ignore
    geomcol.AddGeometry(geom) |> ignore
    geomcol
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Charting
namespace OSGeo
val coordinates : geom:OGR.Geometry -> (float * float) list

Full name: Plot-geometries.coordinates


 Extracts the geometry's coordinates as a list of tuples made of
 longitude and latitude.
val geom : OGR.Geometry
namespace OSGeo.OGR
Multiple items
type Geometry =
  new : type:wkbGeometryType -> Geometry + 2 overloads
  member AddGeometry : other:Geometry -> int
  member AddGeometryDirectly : other_disown:Geometry -> int
  member AddPoint : x:float * y:float * z:float -> unit
  member AddPoint_2D : x:float * y:float -> unit
  member Area : unit -> float
  member AssignSpatialReference : reference:SpatialReference -> unit
  member Boundary : unit -> Geometry
  member Buffer : distance:float * quadsecs:int -> Geometry
  member Centroid : unit -> Geometry
  ...

Full name: OSGeo.OGR.Geometry

--------------------
OGR.Geometry(type: OGR.wkbGeometryType) : unit
OGR.Geometry(cPtr: nativeint, cMemoryOwn: bool, parent: obj) : unit
OGR.Geometry(type: OGR.wkbGeometryType, wkt: string, wkb: int, wkb_buf: nativeint, gml: string) : unit
val last : int
OGR.Geometry.GetPointCount() : int
val i : int
val p : float []
OGR.Geometry.GetPoint(iPoint: int, argout: float []) : unit
val env : geom:OGR.Geometry -> OGR.Envelope

Full name: Plot-geometries.env


 Extracs the bounding box of the geometry
val res : OGR.Envelope
Multiple items
type Envelope =
  new : unit -> Envelope + 1 overload
  member Dispose : unit -> unit
  member MaxX : float with get, set
  member MaxY : float with get, set
  member MinX : float with get, set
  member MinY : float with get, set
  static member getCPtr : obj:Envelope -> HandleRef
  static member getCPtrAndDisown : obj:Envelope * parent:obj -> HandleRef
  static member getCPtrAndSetReference : obj:Envelope * parent:obj -> HandleRef

Full name: OSGeo.OGR.Envelope

--------------------
OGR.Envelope() : unit
OGR.Envelope(cPtr: nativeint, cMemoryOwn: bool, parent: obj) : unit
OGR.Geometry.GetEnvelope(env: OGR.Envelope) : unit
val resize : zoom:float -> en:OGR.Envelope -> OGR.Envelope

Full name: Plot-geometries.resize


 Resizes the envelope based on a choosen zoom
val zoom : float
val en : OGR.Envelope
val dx : float
property OGR.Envelope.MaxX: float
property OGR.Envelope.MinX: float
val dy : float
property OGR.Envelope.MaxY: float
property OGR.Envelope.MinY: float
val xMargin : float
val yMargin : float
val createChart : geom:OGR.Geometry -> ChartTypes.GenericChart

Full name: Plot-geometries.createChart


 Creates a chart for the geometry
val coords : (float * float) list
OGR.Geometry.GetGeometryType() : OGR.wkbGeometryType
type wkbGeometryType =
  | wkbUnknown = 0
  | wkbPoint = 1
  | wkbLineString = 2
  | wkbPolygon = 3
  | wkbMultiPoint = 4
  | wkbMultiLineString = 5
  | wkbMultiPolygon = 6
  | wkbGeometryCollection = 7
  | wkbNone = 100
  | wkbLinearRing = 101
  ...

Full name: OSGeo.OGR.wkbGeometryType
field OGR.wkbGeometryType.wkbPoint = 1
field OGR.wkbGeometryType.wkbPoint25D = -2147483647
type Chart =
  static member Area : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member Area : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member Bar : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member Bar : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> GenericChart
  static member BoxPlotFromData : data:seq<#key * #seq<'a2>> * ?Name:string * ?Title:string * ?Color:Color * ?XTitle:string * ?YTitle:string * ?Percentile:int * ?ShowAverage:bool * ?ShowMedian:bool * ?ShowUnusualValues:bool * ?WhiskerPercentile:int -> GenericChart (requires 'a2 :> value)
  static member BoxPlotFromStatistics : data:seq<#key * #value * #value * #value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?Percentile:int * ?ShowAverage:bool * ?ShowMedian:bool * ?ShowUnusualValues:bool * ?WhiskerPercentile:int -> GenericChart
  static member Bubble : data:seq<#value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart
  static member Bubble : data:seq<#key * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string * ?BubbleMaxSize:int * ?BubbleMinSize:int * ?BubbleScaleMax:float * ?BubbleScaleMin:float * ?UseSizeForLabel:bool -> GenericChart
  static member Candlestick : data:seq<#value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
  static member Candlestick : data:seq<#key * #value * #value * #value * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:Color * ?XTitle:string * ?YTitle:string -> CandlestickChart
  ...

Full name: FSharp.Charting.Chart
static member Chart.Point : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:System.Drawing.Color * ?XTitle:string * ?YTitle:string * ?MarkerColor:System.Drawing.Color * ?MarkerSize:int -> ChartTypes.GenericChart
static member Chart.Point : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:System.Drawing.Color * ?XTitle:string * ?YTitle:string * ?MarkerColor:System.Drawing.Color * ?MarkerSize:int -> ChartTypes.GenericChart
static member Chart.Line : data:Deedle.Series<'K,#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:System.Drawing.Color * ?XTitle:string * ?YTitle:string -> ChartTypes.GenericChart (requires equality and 'K :> key)
static member Chart.Line : data:seq<#value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:System.Drawing.Color * ?XTitle:string * ?YTitle:string -> ChartTypes.GenericChart
static member Chart.Line : data:seq<#key * #value> * ?Name:string * ?Title:string * ?Labels:#seq<string> * ?Color:System.Drawing.Color * ?XTitle:string * ?YTitle:string -> ChartTypes.GenericChart
val createCharts : xs:ChartTypes.GenericChart list -> geom:OGR.Geometry -> ChartTypes.GenericChart list

Full name: Plot-geometries.createCharts
val xs : ChartTypes.GenericChart list
val count : int
OGR.Geometry.GetGeometryCount() : int
OGR.Geometry.GetGeometryRef(geom: int) : OGR.Geometry
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val concat : lists:seq<'T list> -> 'T list

Full name: Microsoft.FSharp.Collections.List.concat
val plotAt : zoom:float -> geom:OGR.Geometry -> ChartTypes.GenericChart

Full name: Plot-geometries.plotAt


 Plots a geometry at a specified zoom
val charts : ChartTypes.GenericChart list
val spaceSize : OGR.Envelope
static member Chart.Combine : charts:seq<ChartTypes.GenericChart> -> ChartTypes.GenericChart
val plot : g:OGR.Geometry -> ChartTypes.GenericChart

Full name: Plot-geometries.plot


 Plots a geometry at a zoom of 80%
val g : OGR.Geometry
val fill : geom:OGR.Geometry -> OGR.Geometry

Full name: Plot-geometries.fill
val geomEnv : OGR.Envelope
val xi : float
val lines : OGR.Geometry list
val i : float
val x : float
val line : OGR.Geometry
field OGR.wkbGeometryType.wkbLineString = 2
OGR.Geometry.AddPoint(x: float, y: float, z: float) : unit
val intersection : OGR.Geometry
OGR.Geometry.Intersection(other: OGR.Geometry) : OGR.Geometry
val geomcol : OGR.Geometry
field OGR.wkbGeometryType.wkbGeometryCollection = 7
val l : OGR.Geometry
OGR.Geometry.AddGeometry(other: OGR.Geometry) : int
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
namespace Fsharp.Gdal
F# Project
Fork me on GitHub