using FieldLogger.Models; using FieldLogger.ViewModels; #if WINDOWS using System.Text.Json; #else using Microsoft.Maui.Controls.Maps; using Microsoft.Maui.Maps; using Map = Microsoft.Maui.Controls.Maps.Map; #endif namespace FieldLogger.Views; public partial class MapPage : ContentPage { private readonly MapViewModel _viewModel; #if WINDOWS private WebView? _webView; private bool _webMapReady; private List? _pendingPoints; #else private Map? _map; #endif public MapPage(MapViewModel viewModel) { InitializeComponent(); BindingContext = _viewModel = viewModel; } protected override async void OnAppearing() { base.OnAppearing(); await RefreshMapAsync(); } private async void OnRefreshClicked(object? sender, EventArgs e) { await RefreshMapAsync(); } private async Task RefreshMapAsync() { await EnsureMapCreatedAsync(); var points = await _viewModel.LoadPointsAsync(); ShowPoints(points); } #if WINDOWS private async Task EnsureMapCreatedAsync() { if (_webView is not null) return; var apiKey = _viewModel.Settings.GoogleMapsApiKey; if (string.IsNullOrWhiteSpace(apiKey)) { MapHost.Content = new Label { Text = "Google Maps API key is not set.\nEnter your Maps JavaScript API key on the Settings page.", HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center, HorizontalTextAlignment = TextAlignment.Center, }; return; } using var stream = await FileSystem.OpenAppPackageFileAsync("map.html"); using var reader = new StreamReader(stream); var html = (await reader.ReadToEndAsync()).Replace("%%GOOGLE_MAPS_API_KEY%%", apiKey); _webView = new WebView { Source = new HtmlWebViewSource { Html = html } }; _webView.Navigated += async (_, _) => { _webMapReady = true; if (_pendingPoints is not null) { var pts = _pendingPoints; _pendingPoints = null; await PushPointsToWebAsync(pts); } }; MapHost.Content = _webView; } private async void ShowPoints(List points) { if (_webView is null) return; if (!_webMapReady) { _pendingPoints = points; return; } await PushPointsToWebAsync(points); } private async Task PushPointsToWebAsync(List points) { var payload = points.Select(p => new { id = p.Id, lat = p.Latitude, lng = p.Longitude, utility = ((UmUtility)p.Utility).ToString(), depth = p.DepthRaw, current = p.CurrentRaw, frequency = p.Frequency, fix = ((GnssFixStatus)p.FixStatus).ToString(), hrms = p.Hrms, time = p.TimestampUtc.ToLocalTime().ToString("g"), }); var json = JsonSerializer.Serialize(payload); await _webView!.EvaluateJavaScriptAsync($"setPoints({json})"); } #else private Task EnsureMapCreatedAsync() { if (_map is null) { _map = new Map { MapType = MapType.Hybrid, IsShowingUser = false }; MapHost.Content = _map; } return Task.CompletedTask; } private void ShowPoints(List points) { if (_map is null) return; _map.Pins.Clear(); foreach (var p in points) { _map.Pins.Add(new Pin { Label = $"#{p.Id} {(UmUtility)p.Utility} · depth {p.DepthRaw}", Address = $"{(GnssFixStatus)p.FixStatus} · ±{p.Hrms:F3} m · {p.TimestampUtc.ToLocalTime():g}", Location = new Location(p.Latitude, p.Longitude), }); } if (points.Count > 0) { var minLat = points.Min(p => p.Latitude); var maxLat = points.Max(p => p.Latitude); var minLon = points.Min(p => p.Longitude); var maxLon = points.Max(p => p.Longitude); var center = new Location((minLat + maxLat) / 2, (minLon + maxLon) / 2); var spanKm = Math.Max(0.05, Location.CalculateDistance(minLat, minLon, maxLat, maxLon, DistanceUnits.Kilometers) * 0.7); _map.MoveToRegion(MapSpan.FromCenterAndRadius(center, Distance.FromKilometers(spanKm))); } } #endif }