Windows-Auto-Night-Mode icon indicating copy to clipboard operation
Windows-Auto-Night-Mode copied to clipboard

Migrate from Bing Maps to Azure Maps

Open Jay-o-Way opened this issue 7 months ago • 3 comments

Migrate from Bing Maps to Azure Maps:

  • https://learn.microsoft.com/azure/azure-maps/migrate-bing-maps-overview

These APIs are deprecated. They are not likely to get fixed and you're not likely to get any support for them. Time to find an alternative I'm afraid.

  • source: https://github.com/microsoft/microsoft-ui-xaml/issues/10229#issuecomment-2540275280

[!IMPORTANT] Important

Bing Maps for Enterprise service retirement

The UWP MapControl and map services from the Windows.Services.Maps namespaces rely on Bing Maps. Bing Maps for Enterprise is deprecated and will be retired, at which point the MapControl and services will no longer receive data.

For more information, see the Bing Maps Developer Center and Bing Maps documentation.

https://learn.microsoft.com/en-us/uwp/api/windows.services.maps?view=winrt-26100#remarks

Jay-o-Way avatar Apr 14 '25 18:04 Jay-o-Way

I copied "Azure" from the comment(s), but if there's another simple server, that's also fine with me, of course.

Jay-o-Way avatar Apr 16 '25 08:04 Jay-o-Way

Today, Copilot gave me a few very simple PowerShell suggestions:

Since you want automatic location detection in the background, we’ll tap into Windows’ built-in geolocation capabilities using PowerShell. Here's a clean and relatively lightweight approach:

🧭 Step 1: Automatically Fetch Location (Lat/Lon)

Windows exposes location data via the System.Device.Location namespace. You can access it in PowerShell like this:

Add-Type -AssemblyName System.Device

$GeoWatcher = New-Object System.Device.Location.GeoCoordinateWatcher
$GeoWatcher.Start()

while (($GeoWatcher.Status -ne 'Ready') -and ($GeoWatcher.Permission -ne 'Denied')) {
    Start-Sleep -Milliseconds 100
}

if ($GeoWatcher.Permission -eq 'Denied') {
    Write-Error 'Access Denied for Location Information'
} else {
    $location = $GeoWatcher.Position.Location
    $latitude = $location.Latitude
    $longitude = $location.Longitude
    Write-Output "Latitude: $latitude, Longitude: $longitude"
}

✅ This script runs silently and fetches your current coordinates. Make sure location services are enabled in Windows Settings → Privacy → Location.

🌅 Step 2: Get Sunrise/Sunset Times

Use the Sunrise-Sunset API — it’s free and doesn’t require an API key. Here’s how to query it:

$apiUrl = "https://api.sunrise-sunset.org/json?lat=$latitude&lng=$longitude&formatted=0"
$response = Invoke-RestMethod -Uri $apiUrl

$sunriseUTC = [datetime]$response.results.sunrise
$sunsetUTC = [datetime]$response.results.sunset

# Convert to local time
$sunriseLocal = $sunriseUTC.ToLocalTime()
$sunsetLocal = $sunsetUTC.ToLocalTime()

Write-Output "Sunrise: $sunriseLocal"
Write-Output "Sunset: $sunsetLocal"

🎨 Step 3: Switch Windows Theme Based on Time

You can toggle between light and dark modes by editing the registry and restarting explorer.exe. Here’s a basic example:

function Set-DarkMode {
    Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" -Name "AppsUseLightTheme" -Value 0
    Stop-Process -Name explorer -Force
    Start-Process explorer.exe
}

function Set-LightMode {
    Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" -Name "AppsUseLightTheme" -Value 1
    Stop-Process -Name explorer -Force
    Start-Process explorer.exe
}

$currentTime = Get-Date
if ($currentTime -gt $sunriseLocal -and $currentTime -lt $sunsetLocal) {
    Set-LightMode
} else {
    Set-DarkMode
}

🕒 Step 4: Automate It

Use Task Scheduler to run this script every hour or at login. You can compile it into a .ps1 file and schedule it with highest privileges.

Jay-o-Way avatar Aug 22 '25 17:08 Jay-o-Way

And for C# code:

let’s build a C# console app that:

  1. Automatically fetches your geolocation (latitude/longitude)
  2. Queries the Sunrise-Sunset API
  3. Compares current time to sunrise/sunset
  4. Switches Windows theme accordingly

🧠 Key Notes Before We Dive In

• Geolocation: Windows doesn’t expose location via .NET directly, so we’ll use IP-based geolocation via a free API like ip-api.com. • Theme Switching: We’ll modify the registry and restart explorer.exe to apply the theme. • Scheduling: You can run this app periodically via Task Scheduler.

💻 Full C# Console App

using System;
using System.Net.Http;
using System.Text.Json;
using Microsoft.Win32;
using System.Diagnostics;

class Program
{
    static async System.Threading.Tasks.Task Main()
    {
        var client = new HttpClient();

        // Step 1: Get location via IP
        var geoResponse = await client.GetStringAsync("http://ip-api.com/json/");
        var geoData = JsonDocument.Parse(geoResponse).RootElement;
        double lat = geoData.GetProperty("lat").GetDouble();
        double lon = geoData.GetProperty("lon").GetDouble();

        // Step 2: Get sunrise/sunset times
        string apiUrl = $"https://api.sunrise-sunset.org/json?lat={lat}&lng={lon}&formatted=0";
        var sunResponse = await client.GetStringAsync(apiUrl);
        var sunData = JsonDocument.Parse(sunResponse).RootElement.GetProperty("results");

        DateTime sunriseUtc = DateTime.Parse(sunData.GetProperty("sunrise").GetString());
        DateTime sunsetUtc = DateTime.Parse(sunData.GetProperty("sunset").GetString());

        DateTime sunriseLocal = sunriseUtc.ToLocalTime();
        DateTime sunsetLocal = sunsetUtc.ToLocalTime();
        DateTime now = DateTime.Now;

        Console.WriteLine($"Sunrise: {sunriseLocal}, Sunset: {sunsetLocal}, Now: {now}");

        // Step 3: Switch theme
        bool isDaytime = now > sunriseLocal && now < sunsetLocal;
        SetTheme(isDaytime);
    }

    static void SetTheme(bool useLight)
    {
        const string personalizeKey = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
        using (RegistryKey key = Registry.CurrentUser.OpenSubKey(personalizeKey, true))
        {
            if (key != null)
            {
                key.SetValue("AppsUseLightTheme", useLight ? 1 : 0, RegistryValueKind.DWord);
                key.SetValue("SystemUsesLightTheme", useLight ? 1 : 0, RegistryValueKind.DWord);
            }
        }

        // Restart explorer to apply changes
        foreach (var process in Process.GetProcessesByName("explorer"))
        {
            process.Kill();
        }
        Process.Start("explorer.exe");
    }
}

🛠️ How to Use

  1. Compile with .NET (e.g., dotnet build)
  2. Run manually or schedule via Task Scheduler
  3. Make sure location services are enabled, or IP-based geolocation is acceptable

Jay-o-Way avatar Aug 22 '25 17:08 Jay-o-Way

✅ Safer Alternatives

If you want to avoid killing explorer.exe, here are gentler options:

  1. Use SendMessageTimeout to broadcast a settings change This can sometimes trigger a UI refresh without restarting Explorer:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam,
 uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);

const uint WM_SETTINGCHANGE = 0x001A;
const uint SMTO_ABORTIFHUNG = 0x0002;

SendMessageTimeout(new IntPtr(0xffff), WM_SETTINGCHANGE, UIntPtr.Zero, IntPtr.Zero,
 SMTO_ABORTIFHUNG, 100, out _);
  1. Prompt the user to log out or restart manually
  2. Display a message box explaining the change and asking for confirmation before restarting Explorer

Jay-o-Way avatar Aug 22 '25 17:08 Jay-o-Way

Couple of points from the linked wiki:

Your app must be authenticated before it can use the MapControl and map services in the Windows.Services.Maps namespace. To authenticate your app, you must specify a maps authentication key.

@Armin2208 We never acquired such an authentication key, right? Strange that it had worked before for many years.

@Jay-o-Way Concerning the deprecation of the Maps API and the UWP controls:

For the backend lat/lon calculation, we use Windows.Devices.Geolocation. This one is not deprecated and does not rely on an external maps service. It's part of the OS geolocation functionality.

As such, the migration we're talking about here is really just about displaying the city name in the UI.

It appears that using Azure Maps is not an option, it requires us to provision an Azure or Bing Maps API key. https://learn.microsoft.com/en-us/azure/azure-maps/migrate-find-location-by-point If we don't find a good alternative to it, my suggestion would just be to display the retrieved Lat/Lon values in auto gelocation mode.

Spiritreader avatar Aug 30 '25 00:08 Spiritreader

UPDATE: I've pulled a newer geojson version, and we now have country-level translation!

Location Mapping is now done offline via a GIS database.

The GIS database comes with the downside that names aren't translated (I'm using the GeoJSON version due to deserialization issues with .dbf files).

But it's a better solution that nothing, is completely free, the Natural Earth database is very permissive with allowed redistribution, and it's fast!

Image

Spiritreader avatar Sep 12 '25 21:09 Spiritreader