UniversalDashboard.Map.psm1


if ($Env:Debug -eq $true) {
    $AssetId = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset("http://localhost:10000/map.index.bundle.js")
    [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset("http://localhost:10000/layers.png") | Out-Null
    [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset("http://localhost:10000/marker-shadow.png") | Out-Null
    [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset("http://localhost:10000/marker-icon.png") | Out-Null
    [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset("http://localhost:10000/marker-icon-2x.png") | Out-Null
} else {
    $IndexJs = Get-ChildItem "$PSScriptRoot\index.*.bundle.js"
    $JsFiles = Get-ChildItem "$PSScriptRoot\*.bundle.js"
    $Maps = Get-ChildItem "$PSScriptRoot\*.map"
    $Pngs = Get-ChildItem "$PSScriptRoot\*.png"

    $AssetId = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($IndexJs.FullName)
    
    foreach($item in $JsFiles)
    {
        [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($item.FullName) | Out-Null
    }

    foreach($item in $Maps)
    {
        [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($item.FullName) | Out-Null
    }

    $Pngs = Get-ChildItem "$PSScriptRoot\*.png"
    foreach($item in $Pngs)
    {
        [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($item.FullName) | Out-Null
    }
}
function New-UDMapBaseLayer {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Name,
        [Parameter(Mandatory)]
        [ScriptBlock]$Content,
        [Parameter()]
        [Switch]$Checked
    )

    @{
        type = "map-base-layer"
        isPlugin = $true
        assetId = $AssetId
        id = $Id
        name = $Name
        content = & $Content
        checked = $Checked.IsPresent
    }
}
function New-UDMapMarkerClusterLayer {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [Hashtable[]]$Markers,
        [Parameter()]
        [int]$MinimumClusterSize = 2,
        [Parameter()]
        [int]$GridSize = 60
    )

    @{
        type = "map-cluster-layer"
        isPlugin = $true
        assetId = $assetId 

        id = $id 
        markers = $Markers
        minClusterSize = $MinimumClusterSize
        gridSize = $GridSize
    }
}
function New-UDMapFeatureGroup 
{
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [Hashtable]$Popup,
        [Parameter(Mandatory)]
        [ScriptBlock]$Content
    )

    End {
        @{
            type = 'map-feature-group'
            id = $id 
            isPlugin = $true
            assetId = $AssetId
            content = & $Content 
            popup = $Popup
        }
    }
}
function ConvertFrom-GeoJson {
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [PSCustomObject[]]$GeoJson,
        [Parameter()]
        [PSCustomObject[]]$Icons
    )

    Process {
        $Json = $GeoJson 

        $Json | ForEach-Object {
            if ($_.type -eq 'FeatureCollection') {
                $features = foreach($Feature in $_.features)
                {
                    $Feature | ConvertFrom-GeoJson -Icons $Icons
                }

                New-UDMapOverlay -Name $_.properties.name -Content {
                    New-UDMapFeatureGroup -Content { $features }
                } -Checked
            }

            if ($_.type -eq 'feature') {
                $Geometry = $_.geometry | ConvertFrom-GeoJson -Icons $Icons
                
                if ($_.properties.DisplayText) {
                    $Geometry.Popup = New-UDMapPopup -Content { New-UDHtml $_.properties.DisplayText }
                }

                if ($_.style.color -and $Geometry.type -ne "map-marker") {
                    $Geometry.FillColor = $_.style.color
                    $Geometry.Color = $_.style.color

                    if ($_.style.weight) {
                        $Geometry.Weight = $_.style.weight
                    }
                }

                if ($_.style.color -and $Geometry.type -eq "map-marker") {

                    $iconName = $_.style.color
                    $Icon = $Icons | Where-Object {
                        $_.IconName -eq $iconName
                    }

                    if ($null -ne $Icon) {
                        $Geometry.Icon = New-UDMapIcon -Url "http://emaps.papertransport.com/e_img/$($Icon.IconFileName)" -Width $Icon.IconWidth -Height $Icon.IconHeight -AnchorX $Icon.IconAnchorX -AnchorY $Icon.IconHeight -PopupAnchorX $Icon.IconPopupX -PopupAnchorY $Icon.IconPopupY
                    }
                }

                $Geometry
            }

            if ($_.type -eq 'polygon') {
                $Coordinates = @()
                $_.coordinates[0] | ForEach-Object {
                    $temp = $_[0]
                    $_[0] = $_[1]
                    $_[1] = $temp

                    $Coordinates += ,$_
                }

                New-UDMapVectorLayer -Polygon -Positions $Coordinates -FillOpacity .5
            }

            if ($_.type -eq 'point') {
                $Coordinates = $_.coordinates

                New-UDMapMarker -Latitude $coordinates[1] -Longitude $coordinates[0] -Zindex $_.style.zIndexOffset
            }

            if ($_.type -eq 'linestring') {
                $Coordinates = $_.coordinates

                New-UDMapMarker -Latitude $coordinates[1] -Longitude $coordinates[0] -Zindex $_.style.zIndexOffset
            }

            if ($_.type -eq 'MultiLineString') {
                $Coordinates = @()
                foreach($array in $_.coordinates) {
                    foreach($arrayInArray in $array) {
                        $temp = $arrayInArray[0]
                        $arrayInArray[0] = $arrayInArray[1]
                        $arrayInArray[1] = $temp
    
                        $Coordinates += ,$arrayInArray
                    }
                }

                New-UDMapVectorLayer -Polyline -Positions $Coordinates
            }
        }
    }
}
function New-UDMapHeatmapLayer {
    param(
        [Parameter(Mandatory)]
        $Points,
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [double]$MaxIntensity,
        [Parameter()]
        [double]$Radius,
        [Parameter()]
        [int]$MaxZoom,
        [Parameter()]
        [double]$MinOpacity,
        [Parameter()]
        [int]$Blur,
        [Parameter()]
        [Hashtable]$Gradient
    )

    $Options = @{
        type = 'map-heatmap-layer'
        isPlugin = $true
        assetId = $AssetId
    }

    foreach($boundParameter in $PSCmdlet.MyInvocation.BoundParameters.GetEnumerator()) {
        $Options[[char]::ToLowerInvariant($boundParameter.Key[0]) + $boundParameter.Key.Substring(1)] = $boundParameter.Value
    }

    $Options
}
function New-UDMapIcon {
    param(
        [Parameter(Mandatory)]
        [string]$Url,
        [Parameter()]
        [int]$Height,
        [Parameter()]
        [int]$Width,
        [Parameter()]
        [int]$AnchorX,
        [Parameter()]
        [int]$AnchorY,
        [Parameter()]
        [int]$PopupAnchorX,
        [Parameter()]
        [int]$PopupAnchorY
    )

    $Options = @{
    }

    foreach($boundParameter in $PSCmdlet.MyInvocation.BoundParameters.GetEnumerator()) {
        $Options[[char]::ToLowerInvariant($boundParameter.Key[0]) + $boundParameter.Key.Substring(1)] = $boundParameter.Value
    }

    $Options
}
function New-UDMapLayerControl {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [ValidateSet("topright", "topleft", "bottomright", "bottomleft")]
        [string]$Position = "topright",
        [Parameter()]
        [ScriptBlock]$Content
    )

    @{
        type = 'map-layer-control'
        isPlugin = $true
        assetId = $AssetId
        id = $Id
        content = & $Content
        position = $Position
    }
}
function New-UDMap {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [float]$Longitude,
        [Parameter()]
        [float]$Latitude,
        [Parameter()]
        [int]$Zoom,
        [Parameter()]
        [string]$Height = '500px',
        [Parameter()]
        [string]$Width = '100%',
        [Parameter(Mandatory)]
        [object]$Endpoint,
        [ValidateSet("topright", "topleft", "bottomright", "bottomleft")]
        [string]$ZoomControlPosition = "topright",
        [ValidateSet("topright", "topleft", "bottomright", "bottomleft", "hide")]
        [string]$ScaleControlPosition = "bottomleft",
        [Parameter()]
        [Switch]$Animate,
        [Parameter()]
        [int]$MaxZoom = 18
    )

    End {

        if ($Endpoint -is [scriptblock]) {
            $Endpoint = New-UDEndpoint -Endpoint $Endpoint -Id $Id
        }
        elseif ($Endpoint -isnot [UniversalDashboard.Models.Endpoint]) {
            throw "Endpoint must be a script block or UDEndpoint"
        }

        @{
            assetId = $AssetId 
            isPlugin = $true 
            type = "ud-map"
            id = $Id

            longitude = $Longitude
            latitude = $Latitude
            zoom = $Zoom
            height = $Height
            width = $Width
            zoomControlPosition = $ZoomControlPosition
            animate = $Animate.IsPresent
            scaleControlPosition = $ScaleControlPosition
            maxZoom = $MaxZoom
        }
    }
}
function New-UDMapMarker {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(ParameterSetName = "LatLng", Mandatory)]
        [float]$Longitude,
        [Parameter(ParameterSetName = "LatLng", Mandatory)]
        [float]$Latitude,
        [Parameter()]
        [string]$Attribution,
        [Parameter()]
        [int]$Opacity,
        [Parameter()]
        [int]$ZIndex,
        [Parameter()]
        [Hashtable]$Popup,
        [Parameter()]
        [Hashtable]$Icon,
        [Parameter(ParameterSetName = "GeoJSON", Mandatory)]
        [string]$GeoJSON
    )

    if ($PSCmdlet.ParameterSetName -eq 'GeoJSON') {
        $Json = $GeoJSON | ConvertFrom-Json
        $Coordinates = $Json.Geometry.Coordinates
        $Latitude = $Coordinates[1]
        $Longitude = $Coordinates[0]
    }

    @{
        type = "map-marker"
        isPlugin = $true
        assetId = $AssetId

        id = $id 
        longitude = $Longitude
        latitude = $Latitude
        attribution = $Attribution
        opacity = $Opacity
        zIndex = $ZIndex
        popup = $Popup 
        icon = $Icon
    }
}
function New-UDMapOverlay {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Name,
        [Parameter(Mandatory)]
        [ScriptBlock]$Content,
        [Parameter()]
        [Switch]$Checked
    )

    @{
        type = 'map-overlay'
        isPlugin = $true
        assetId = $AssetId

        id = $id
        name = $Name 
        content = & $Content
        checked = $Checked.IsPresent
    }
}
function New-UDMapPopup {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [ScriptBlock]$Content,
        [Parameter()]
        [float]$Longitude,
        [Parameter()]
        [float]$Latitude,
        [Parameter()]
        [int]$MaxWidth,
        [Parameter()]
        [int]$MinWidth
    )

    $Options = @{
        type = "map-popup"
        isPlugin = $true
        assetId = $AssetId

        id = $id
        content = & $Content

        maxWidth = $MaxWidth
        minWidth = $MinWidth
    }

    if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Longitude")) {
        $Options["longitude"] = $Longitude
    }

    if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Latitude")) {
        $Options["latitude"] = $Latitude
    }

    $Options
}
function New-UDMapRasterLayer {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(ParameterSetName = "Generic")]
        [string]$TileServer = 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
        [Parameter(ParameterSetName = "Bing", Mandatory)]
        [string]$ApiKey,
        [Parameter(ParameterSetName = "Bing")]
        [ValidateSet("Aerial", "AerialWithLabels", "AerialWithLabelsOnDemand", "CanvasDark", "CanvasLight", "CanvasGray", "Road")]
        [string]$Type = "Aerial",
        [Parameter(ParameterSetName = "Bing", Mandatory)]
        [Switch]$Bing,
        [Parameter()]
        [string]$Attribution,
        [Parameter()]
        [int]$Opacity,
        [Parameter()]
        [int]$ZIndex,
        [Parameter()]
        [string]$Name
    )

    @{
        type = "map-raster-layer"
        isPlugin = $true
        assetId = $AssetId

        id = $id
        tileServer = $TileServer
        apiKey = $ApiKey
        attribution = $Attribution
        opacity = $Opactiy
        zIndex = $ZIndex
        name = $Name
        bing = $Bing.IsPresent
        mapType = $Type 
    }
}
function New-UDMapVectorLayer {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter()]
        [UniversalDashboard.Models.DashboardColor]$Color = 'black',
        [Parameter()]
        [UniversalDashboard.Models.DashboardColor]$FillColor = 'black',
        [Parameter()]
        [double]$FillOpacity = 0.5,
        [Parameter()]
        [int]$Weight = 3,
        [Parameter()]
        [double]$Opacity = 1.0,
        [Parameter(ParameterSetName = 'Circle', Mandatory)]
        [Switch]$Circle,
        [Parameter(ParameterSetName = 'Circle', Mandatory)]
        [double]$Latitude,
        [Parameter(ParameterSetName = 'Circle', Mandatory)]
        [double]$Longitude,
        [Parameter(ParameterSetName = 'Circle', Mandatory)]
        [int]$Radius,
        [Parameter(ParameterSetName = 'Polyline', Mandatory)]
        [Switch]$Polyline,
        [Parameter(ParameterSetName = 'Polygon', Mandatory)]
        [Switch]$Polygon,
        [Parameter(ParameterSetName = 'Polyline', Mandatory)]        
        [Parameter(ParameterSetName = 'Polygon', Mandatory)]        
        [object]$Positions,
        [Parameter(ParameterSetName = 'Rectangle', Mandatory)]
        [Switch]$Rectangle,
        [Parameter(ParameterSetName = 'Rectangle', Mandatory)]
        [double]$LatitudeTopLeft,
        [Parameter(ParameterSetName = 'Rectangle', Mandatory)]
        [double]$LongitudeTopLeft,
        [Parameter(ParameterSetName = 'Rectangle', Mandatory)]
        [double]$LatitudeBottomRight,
        [Parameter(ParameterSetName = 'Rectangle', Mandatory)]
        [double]$LongitudeBottomRight,
        [Parameter(ParameterSetName = 'Circle')]
        [object]$Popup,
        [Parameter(ParameterSetName = 'GeoJSON', Mandatory)]
        [string]$GeoJSON
    )

    if ($PSCmdlet.ParameterSetName -eq 'GeoJSON') {
        $Json = $GeoJSON | ConvertFrom-Json
        if ($Json.Geometry.Type -eq 'multilinestring') 
        {
            $Coordinates = @()
            foreach($array in $json.geometry.coordinates) {
                foreach($arrayInArray in $array) {
                    $temp = $arrayInArray[0]
                    $arrayInArray[0] = $arrayInArray[1]
                    $arrayInArray[1] = $temp
                }
                $Coordinates += ,$array
            }

            $Positions = $Coordinates
            $Polyline = [Switch]::Present
        }

        if ($Json.Geometry.Type -eq 'linestring') 
        {
            $Coordinates = @()
            $json.geometry.coordinates | ForEach-Object {
                $temp = $_[0]
                $_[0] = $_[1]
                $_[1] = $temp
                $Coordinates += ,$_
            }
            $Positions = $Coordinates
            $Polyline = [Switch]::Present
        }

        if ($Json.Geometry.Type -eq 'polygon') 
        {
            $Coordinates = @()
            $json.geometry.coordinates[0] | ForEach-Object {
                $temp = $_[0]
                $_[0] = $_[1]
                $_[1] = $temp
                $Coordinates += ,$_
            }
            $Positions = $Coordinates
            $Polygon = [Switch]::Present
        }
    }

    @{
        type = "map-vector-layer"
        isPlugin = $true
        assetId = $AssetId
        id = $Id

        color = $Color.HtmlColor
        fillColor = $FillColor.HtmlColor
        fillOpacity = $FillOpacity
        weight = $Weight
        opacity = $Opacity
        circle = $Circle.IsPresent
        latitude = $Latitude
        longitude = $Longitude
        radius = $Radius
        polyline = $Polyline.IsPresent
        polygon = $Polygon.IsPresent
        positions = $Positions
        rectangle = $Rectangle.IsPresent
        latitudeTopLeft = $LatitudeTopLeft
        longitudeTopLeft = $LongitudeTopLeft
        latitudeBottomRight = $LatitudeBottomRight
        longitudeBottomRight = $LongitudeBottomRight
        popup = $Popup
    }
}