11# Requires -Version 7.0
22
3+ # Azure Maps Custom Feature States (version 1.0-rc.1)
4+ # Copyright (c) Microsoft Corporation. All rights reserved.
5+ # https://github.com/Azure-Samples/Azure-Maps-Custom-Feature-States
6+ #
7+ # This source code is licensed under the MIT license found in the
8+ # LICENSE file in the root directory of this source tree.
9+
310[CmdletBinding ()]
411param (
512 [Parameter (Mandatory = $false )]
@@ -44,13 +51,39 @@ function Test-LastExitCode
4451 }
4552}
4653
54+
55+ function Process-Zip-Operation
56+ {
57+ param ($uri , $authPartUri , $outputPath , $operationName )
58+
59+ Write-Log - Message " - Processing Zip Operation '$operationName ' at '$uri '" - Logfile $logLocation - Severity Information
60+
61+ $uri = $uri + $authPartUri
62+
63+ $zipPath = GetTempFile(" customfeaturestates_zip_operation.zip" )
64+
65+ $response = Invoke-RestMethod - Uri $uri - Method ' GET' - ContentType " application/zip" - StatusCodeVariable scv - ResponseHeadersVariable responseHeader - SkipHttpErrorCheck - OutFile $zipPath
66+
67+ if ($scv -ne 200 )
68+ {
69+ Write-Err " Failed to send $operationName request. Status code: $scv Response: $ ( ConvertTo-Json $response - Depth 10 ) "
70+ exit 1
71+ }
72+
73+ Expand-Archive $zipPath - DestinationPath $outputPath - Force
74+ Test-LastExitCode
75+
76+ Remove-Item $zipPath | Out-Debug
77+ Test-LastExitCode
78+ }
79+
4780function Process-LRO
4881{
4982 param ($uri , $filePath , $contentType , $responseHeader , $authPartUri , $operationName )
5083
5184 Write-Log - Message " - Processing LRO '$operationName ' at '$uri '" - Logfile $logLocation - Severity Information
5285
53- $uri = $uri + $authPartUri
86+ $uri = $uri + $authPartUri
5487
5588 if ($PSBoundParameters.ContainsKey (" filePath" ))
5689 {
@@ -64,7 +97,7 @@ function Process-LRO
6497 # Check if the response status code is 202 - Accepted
6598 if ($scv -ne 202 )
6699 {
67- Write-Err " Failed to send $operationName request. Status code: $scv Response: $ ( ConvertTo-Json $response ) "
100+ Write-Err " Failed to send $operationName request. Status code: $scv Response: $ ( ConvertTo-Json $response - Depth 10 ) "
68101 exit 1
69102 }
70103
@@ -91,15 +124,15 @@ function Process-LRO
91124 # Check if the response JSON has a "status" field set to "Failed" for failed responses
92125 if ($status -eq " Failed" )
93126 {
94- Write-Err " Failed to create artifact for $operationName request. Status: $ ( $statusResponse.statusMessage ) , Code: $scv , Response: $ ( ConvertTo-Json $statusResponse ) "
127+ Write-Err " Failed to create artifact for $operationName request. Status: $ ( $statusResponse.statusMessage ) , Code: $scv , Response: $ ( ConvertTo-Json $statusResponse - Depth 10 ) "
95128 exit 1
96129 }
97130
98131 $resourceLocation = If ($statusResponse.resourceLocation -like ' ' ) {$rh [" Resource-Location" ]} Else {$statusResponse.resourceLocation }
99132
100133 if ($resourceLocation -like ' ' )
101134 {
102- Write-Err " Failed to determine resource location from response for $operationName request. Response: $ ( ConvertTo-Json $statusResponse ) "
135+ Write-Err " Failed to determine resource location from response for $operationName request. Response: $ ( ConvertTo-Json $statusResponse - Depth 10 ) "
103136 exit 1
104137 }
105138
@@ -222,13 +255,18 @@ try
222255
223256 # Azure Maps Configuration
224257 $azuremapsDrawingPackageUri = If ($DrawingPackageUri -ne " " ) {$DrawingPackageUri } Else {" https://github.com/Azure-Samples/am-creator-indoor-data-examples/raw/master/Drawing%20Package%202.0/Sample%20-%20Contoso%20Drawing%20Package.zip" }
225- $azuremapsSourceLayer = " room" # this may be different for your own map
226258 $azuremapsAPIVersion = " 2023-03-01-preview"
227259
260+ # Map-specific configuration - these may be different for your map
261+ $azureMapsFeatureLayer = " indoor_room_area"
262+ $azureMapsFeatureStatesToColor = @ {" free" = " green" ; " occupied" = " red" }
263+ $azureMapsDefaultFeatureColor = " white"
264+ $azureMapsStyleNames = @ (' light' , ' dark' )
265+
228266 # Geography
229267 $geography = $ (az account list- locations -- query " [?name == '$Location '].{GG:metadata.geographyGroup}" - o tsv)
230268 $azuremapsGeography = If ($geography -like " *Europe*" ) {" eu" } Else {" us" }
231- Write-Info " - Assuming Azure maps geography '$azuremapsGeography ' based on location '$location ', geography '$geography ' "
269+ Write-Info " - Assuming Azure maps geography '$azuremapsGeography ' based on location '$Location ', geography '$geography ' "
232270
233271 # Create a resource group
234272 if ($ (az group exists -- name $group ) -eq ' true' )
@@ -257,17 +295,20 @@ try
257295 else
258296 {
259297 Write-Info " - Creating an Azure Maps creator account named '$azuremapsCreator '..."
260- az maps creator create - g $group -- account- name $azuremaps -- creator- name $azuremapsCreator -- storage- units 2 | Out-Stream
298+ az maps creator create - g $group -- account- name $azuremaps -- creator- name $azuremapsCreator -- storage- units 2 -- location $Location | Out-Stream
261299 Test-LastExitCode
262300 }
263301
264302 # Upload package
265303 $azuremapsDomain = " https://$azuremapsGeography .atlas.microsoft.com"
266304 Write-Info " - Uploading drawing package using creator account from '$azuremapsDrawingPackageUri ' to '$azuremapsDomain '..."
267305 Write-Info " - This operation may take several minutes; please wait..."
306+
307+ $authPart = " &subscription-key=$azuremapssubscriptionkey "
308+
268309 $packageFilePath = GetTempFile(" drawing_package_$Name .zip" )
269310 Invoke-WebRequest " $azuremapsDrawingPackageUri " - OutFile $packageFilePath
270- $authPart = " &subscription-key= $azuremapssubscriptionkey "
311+
271312 $uduri = " $azuremapsDomain /mapData/upload?api-version=1.0&dataFormat=zip"
272313 $conversionUri = " $azuremapsDomain /conversions?api-version=$azuremapsAPIVersion &dwgPackageVersion=2.0"
273314 $datasetUri = " $azuremapsDomain /datasets?api-version=$azuremapsAPIVersion "
279320 $tilesetId = Process - LRO - responseHeader $rh - authPartUri $authPart - uri $tilesetUri " &datasetId=$datasetId " - operationName " Tileset"
280321 $mapConfigurationId = " default_$tilesetId "
281322
323+ # Create modified style with feature-state-based styling rule and associated map configuration
324+ Write-Info " - Modifying map style with custom feature state rules..."
325+ $mapConfigPath = GetTempFile(" customfeaturestates_mapconfig" )
326+ Process - Zip- Operation - authPartUri $authPart - uri " $azuremapsDomain /styles/mapconfigurations/$ ( $mapConfigurationId ) ?api-version=$ ( $azuremapsAPIVersion ) " - outputPath $mapConfigPath - operationName " MapConfiguration"
327+ $mapConfigJson = Get-Content - Path " $mapConfigPath \configuration.json" | ConvertFrom-Json
328+
329+ foreach ($styleName in $azureMapsStyleNames )
330+ {
331+ $styleConfiguration = ($mapConfigJson.configurations | Where-Object { $_.name -eq $styleName } | Select-Object - first 1 )
332+ $styleId = $styleConfiguration.layers [0 ].styleId
333+
334+ $stylePath = GetTempFile(" customfeaturestates_style" )
335+ Process - Zip- Operation - authPartUri $authPart - uri " $azuremapsDomain /styles/$ ( $styleId ) ?api-version=$ ( $azuremapsAPIVersion ) " - outputPath $stylePath - operationName " Styles"
336+ $styleJson = Get-Content - Path " $stylePath \style.json" | ConvertFrom-Json
337+
338+ $styleLayers = $styleJson.layers
339+ $featureLayerIndex = ([Array ]::FindIndex($styleLayers , [System.Predicate [pscustomobject ]]{ $args [0 ].id -eq $azureMapsFeatureLayer } ))
340+ $azuremapsSourceLayer = $styleLayers [$featureLayerIndex ] | Select-Object - ExpandProperty " source-layer"
341+
342+ $jsonToAppend = ' {
343+ "id": ' + " `" $ ( $azureMapsFeatureLayer ) _fs`" ," +
344+ ' "type": "fill",
345+ "filter": [
346+ "==",
347+ "$type",
348+ "Polygon"
349+ ],
350+ "layout": {
351+ "visibility": "visible"
352+ },
353+ "minzoom": 16.0,
354+ "paint": {
355+ "fill-antialias": true,
356+ "fill-color": [
357+ "match",
358+ ["feature-state", "customFeatureState"],' +
359+ ((($azureMapsFeatureStatesToColor.GetEnumerator () | ForEach-Object { " `" $ ( $_.Key ) `" , `" $ ( $_.Value ) `" " }) -join ' , ' ) + " , `" $azureMapsDefaultFeatureColor `" " ) +
360+ ' ],
361+ "fill-opacity": 0.5,
362+ "fill-outline-color": "rgba(120, 120, 120, 1)"
363+ },
364+ "source-layer":' + " `" $azuremapsSourceLayer `" " +
365+ ' }'
366+
367+ $styleJson.layers = @ ($styleLayers [0 .. $featureLayerIndex ]) + ($jsonToAppend | ConvertFrom-Json ) + @ ($styleLayers [($featureLayerIndex + 1 ).. ($styleLayers.length - 1 )])
368+ $styleJson | ConvertTo-Json - Compress - Depth 100 | Out-File " $stylePath \style.json"
369+ Compress-Archive $stylePath \* " $stylePath .zip" - Force | Out-Debug
370+ Test-LastExitCode
371+
372+ $styleUri = " $azuremapsDomain /styles?api-version=$azuremapsAPIVersion &dataFormat=zip"
373+ $styleId = Process - LRO - responseHeader $rh - authPartUri $authPart - uri $styleUri " &alias=fs_$styleId " - filePath " $stylePath .zip" - contentType " application/zip" - operationName " Style"
374+
375+ $styleConfiguration.layers [0 ].styleId = $styleId
376+
377+ Remove-Item $stylePath - Recurse | Out-Debug
378+ Remove-Item " $stylePath .zip" | Out-Debug
379+ }
380+
381+ $mapConfigJson | ConvertTo-Json - Compress - Depth 100 | Out-File " $mapConfigPath \configuration.json"
382+ Compress-Archive $mapConfigPath \* " $mapConfigPath .zip" - Force | Out-Debug
383+ $mapConfigUri = " $azuremapsDomain /styles/mapconfigurations?api-version=$azuremapsAPIVersion &ContentType=application/zip"
384+ $mapConfigurationId = Process - LRO - responseHeader $rh - authPartUri $authPart - uri $mapConfigUri " &alias=fs_$mapConfigurationId " - filePath " $mapConfigPath .zip" - contentType " application/zip" - operationName " MapConfiguration"
385+
386+ Remove-Item $mapConfigPath - Recurse | Out-Debug
387+ Remove-Item " $mapConfigPath .zip" | Out-Debug
388+
282389 # Create SignalR service
283390 Write-Info " - Creating an Azure SignalR service named '$signalr '..."
284391 az signalr create - g $group -- name $signalr -- sku Standard_S1 -- service- mode Default -- location $Location | Out-Stream
@@ -315,11 +422,14 @@ try
315422 Start-Sleep - Seconds 5
316423
317424 $principal = (az webapp identity show - g $group -- name $webappname -- query ' principalId' -- output tsv)
425+ $userId = (az ad signed- in- user show -- query ' id' -- output tsv)
318426 $scope = (az maps account show - g $group -- name $azuremaps -- query ' id' -- output tsv)
319427
320- Write-Info " - Assigning Azure Maps Data Reader role to principal..."
428+ Write-Info " - Assigning Azure Maps Data Reader role to principal and current user ..."
321429 az role assignment create -- assignee $principal -- role " Azure Maps Data Reader" -- scope $scope | Out-Stream
322430 Test-LastExitCode
431+ az role assignment create -- assignee $userId -- role " Azure Maps Data Reader" -- scope $scope | Out-Stream
432+ Test-LastExitCode
323433
324434 # Get the Azure Maps Client Id
325435 $azuremapsClientId = (az maps account show - n $azuremaps - g $group -- query ' properties.uniqueId' -- output tsv)
335445 $email = (az rest -- method get -- url ' https://graph.microsoft.com/v1.0/me' -- query ' userPrincipalName' - o tsv)
336446 $domain = $email.Split (' @' )[1 ]
337447
338- az webapp config appsettings set - g $group - n $webappname -- settings Azure:SignalR:ConnectionString= $signalrconnectionstring AzureMaps:Domain= $azuremapsDomain AzureMaps:ClientId= $azuremapsClientId AzureMaps:TokenUrl=/ api/ azuremaps/ token AzureMaps:APIVersion= $azuremapsAPIVersion AzureMaps:MapConfigurationId= $mapConfigurationId AzureMaps:DatasetId= $datasetId AzureMaps:SourceLayer= $azuremapsSourceLayer Database:Name= $DatabaseName Database:ConnectionString= $connectionString AzureAd:Instance= https:// login.microsoftonline.com / AzureAd:Domain= $domain AzureAd:TenantId= $tenantId AzureAd:ClientId= $clientId AzureAd:CallbackPath=/ signin- oidc | Out-Stream
448+ az webapp config appsettings set - g $group - n $webappname -- settings Azure:SignalR:ConnectionString= $signalrconnectionstring AzureMaps:Domain= $azuremapsDomain AzureMaps:ClientId= $azuremapsClientId AzureMaps:TokenUrl=/ api/ azuremaps/ token AzureMaps:APIVersion= $azuremapsAPIVersion AzureMaps:MapConfigurationId= $mapConfigurationId AzureMaps:DatasetId= $datasetId AzureMaps:TilesetId = $tilesetId AzureMaps: SourceLayer= $azuremapsSourceLayer AzureMaps:FeatureLayer = $azuremapsFeatureLayer Database:Name= $DatabaseName Database:ConnectionString= $connectionString AzureAd:Instance= https:// login.microsoftonline.com / AzureAd:Domain= $domain AzureAd:TenantId= $tenantId AzureAd:ClientId= $clientId AzureAd:CallbackPath=/ signin- oidc AzureAd:SignedOutCallbackPath =/ signout - oidc | Out-Stream
339449 Test-LastExitCode
340450
451+ # Write dev config
452+ if ($ (Test-Path - path ' .\FeatureStateService\appsettings.json' - PathType Leaf))
453+ {
454+ $devConfigPath = " .\FeatureStateService\appsettings.Development.json"
455+
456+ Write-Info " - Writing dev config to $devConfigPath ..."
457+
458+ $devConfigJson = [PSCustomObject ]@ {
459+ AzureMaps = [PSCustomObject ]@ {
460+ Domain = $azuremapsDomain
461+ ClientId = $azuremapsClientId
462+ TokenUrl = " /api/azuremaps/token"
463+ APIVersion = $azuremapsAPIVersion
464+ MapConfigurationId = $mapConfigurationId
465+ DatasetId = $datasetId
466+ TilesetId = $tilesetId
467+ SourceLayer = $azuremapsSourceLayer
468+ FeatureLayer = $azuremapsFeatureLayer
469+ }
470+ AzureAd = [PSCustomObject ]@ {
471+ Instance = " https://login.microsoftonline.com/"
472+ Domain = $domain
473+ TenantId = $tenantId
474+ ClientId = $clientId
475+ CallbackPath = " /signin-oidc"
476+ SignedOutCallbackPath = ' /signout-oidc'
477+ }
478+ Database = [PSCustomObject ]@ {
479+ Name = $DatabaseName
480+ ConnectionString = $connectionString
481+ }
482+ }
483+
484+ $devConfigJson | ConvertTo-Json | Out-File $devConfigPath
485+ }
486+
341487 # Deploy Azure Maps Custom Feature States
342488 Write-Info " - Initiating the deployment of the Custom Feature States website..."
343489 $deploymentPath = GetTempFile(" customfeaturestates_binaries.zip" )
354500 else
355501 {
356502 Write-Info " - Downloading deployment zip..."
357- Invoke-WebRequest " https://samples.azuremaps.com/install /customfeaturestates.zip" - OutFile $deploymentPath
503+ Invoke-WebRequest " https://samples.azuremaps.com/customfeaturestates /customfeaturestates.zip" - OutFile $deploymentPath
358504 Test-LastExitCode
359505 }
360506 az webapp deployment source config- zip - g $group - n $webappname -- src $deploymentPath | Out-Stream
0 commit comments