commit fecd0ce96843e0e1b251f8ab1f479539b423e72f Author: Carlos Gutierrez Date: Sun Oct 12 21:42:25 2025 -0400 Integration and add heat map demo - Implemented OpenStreetMap using WebView with Leaflet.js - Added OpenStreetMapView component with interactive map functionality - Created heat map visualization with color-coded intensity - Added 30 dummy location points around San Francisco Bay Area - Implemented location tracking with real-time pin placement - Added comprehensive UI with two-row button layout - Features: Start/Stop tracking, Center map, Demo heat map, Clear demo, Reset map - Added location count display and confirmation dialogs - Updated project structure and documentation - All functionality tested and working on Android emulator diff --git a/LocationTrackerApp/.gitignore b/LocationTrackerApp/.gitignore new file mode 100644 index 0000000..0449018 --- /dev/null +++ b/LocationTrackerApp/.gitignore @@ -0,0 +1,641 @@ +# .NET MAUI / Xamarin +bin/ +obj/ +*.user +*.suo +*.cache +*.dll +*.exe +*.pdb +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc +*.swp +*.tmp +*.userprefs +*.usertasks +*.pidb +*.monotouch-files +*.useros +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio +.vs/ +*.user +*.useros +*.suo +*.cache +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Visual Studio Code +.vscode/ +*.code-workspace + +# JetBrains Rider +.idea/ +*.sln.iml + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# Mono auto generated files +mono_crash.* + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +# Android +*.apk +*.aab +*.dex +*.class +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar +hs_err_pid* + +# Android Studio +.gradle/ +build/ +local.properties +*.iml +.idea/ +*.ipr +*.iws +.navigation/ +captures/ +output.json + +# NDK +obj/ + +# IntelliJ IDEA +.idea/ +*.iml +*.ipr +*.iws +out/ + +# User-specific configurations +.idea/libraries/ +.idea/workspace.xml +.idea/tasks.xml +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/modules.xml +.idea/scopes/scope_settings.xml +.idea/vcs.xml +*.iml + +# OS-specific files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# iOS +*.ipa +*.dSYM.zip +*.dSYM +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# CocoaPods +Pods/ + +# Carthage +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +iOSInjectionProject/ + +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc.index +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these files may be visible to others. +*.azurePubxml + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment the next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +CordovaApp.projitems + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# Configuration files with sensitive data +appsettings.Production.json +appsettings.Staging.json +*.secrets.json + +# MAUI specific +*.appx +*.appxbundle +*.msix +*.msixbundle +*.appinstaller +*.appxmanifest +*.msixmanifest +*.appinstaller + +# MAUI build outputs +bin/ +obj/ +*.user +*.suo +*.cache +*.dll +*.exe +*.pdb +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc +*.swp +*.tmp +*.userprefs +*.usertasks +*.pidb +*.monotouch-files +*.useros +*.sln.docstates + +# MAUI Android specific +*.apk +*.aab +*.dex +*.class +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar +hs_err_pid* + +# MAUI iOS specific +*.ipa +*.dSYM.zip +*.dSYM +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# MAUI macOS specific +.DS_Store +.AppleDouble +.LSOverride +Icon +._* +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/LocationTrackerApp/App.xaml b/LocationTrackerApp/App.xaml new file mode 100644 index 0000000..d14ddee --- /dev/null +++ b/LocationTrackerApp/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/LocationTrackerApp/App.xaml.cs b/LocationTrackerApp/App.xaml.cs new file mode 100644 index 0000000..2850fd5 --- /dev/null +++ b/LocationTrackerApp/App.xaml.cs @@ -0,0 +1,28 @@ +using LocationTrackerApp.Views; +using LocationTrackerApp.ViewModels; +using LocationTrackerApp.Data; + +namespace LocationTrackerApp; + +public partial class App : Application +{ + public App() + { + InitializeComponent(); + } + + protected override Window CreateWindow(IActivationState? activationState) + { + // Get services from DI container + var dbContext = Handler?.MauiContext?.Services?.GetService(); + var mainViewModel = Handler?.MauiContext?.Services?.GetService(); + + if (dbContext != null) + { + // Ensure database is created + _ = Task.Run(async () => await dbContext.EnsureDatabaseCreatedAsync()); + } + + return new Window(new AppShell()); + } +} \ No newline at end of file diff --git a/LocationTrackerApp/AppShell.xaml b/LocationTrackerApp/AppShell.xaml new file mode 100644 index 0000000..d2f2a5f --- /dev/null +++ b/LocationTrackerApp/AppShell.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/LocationTrackerApp/AppShell.xaml.cs b/LocationTrackerApp/AppShell.xaml.cs new file mode 100644 index 0000000..f06eb25 --- /dev/null +++ b/LocationTrackerApp/AppShell.xaml.cs @@ -0,0 +1,14 @@ +using LocationTrackerApp.Views; + +namespace LocationTrackerApp; + +public partial class AppShell : Shell +{ + public AppShell() + { + InitializeComponent(); + + // Register routes + Routing.RegisterRoute(nameof(MainPage), typeof(MainPage)); + } +} diff --git a/LocationTrackerApp/Components/OpenStreetMapView.cs b/LocationTrackerApp/Components/OpenStreetMapView.cs new file mode 100644 index 0000000..bc93c57 --- /dev/null +++ b/LocationTrackerApp/Components/OpenStreetMapView.cs @@ -0,0 +1,368 @@ +using Microsoft.Extensions.Logging; +using LocationTrackerApp.Models; + +namespace LocationTrackerApp.Components; + +/// +/// Custom WebView component for displaying OpenStreetMap +/// +public class OpenStreetMapView : WebView +{ + private readonly ILogger? _logger; + private List _locationData = new(); + private bool _isHeatMapVisible = true; + private bool _isPointsVisible = true; + + /// + /// Gets or sets a value indicating whether the heat map overlay is visible. + /// + public bool IsHeatMapVisible + { + get => _isHeatMapVisible; + set + { + if (_isHeatMapVisible != value) + { + _isHeatMapVisible = value; + UpdateHeatMapVisibility(); + } + } + } + + /// + /// Gets or sets a value indicating whether individual location points are visible. + /// + public bool IsPointsVisible + { + get => _isPointsVisible; + set + { + if (_isPointsVisible != value) + { + _isPointsVisible = value; + UpdatePointsVisibility(); + } + } + } + + /// + /// Default constructor for XAML + /// + public OpenStreetMapView() + { + InitializeMap(); + } + + /// + /// Initializes a new instance of OpenStreetMapView + /// + /// Logger for recording events + public OpenStreetMapView(ILogger logger) : this() + { + _logger = logger; + } + + /// + /// Initializes the map with default settings + /// + private void InitializeMap() + { + // Set default location to San Francisco Bay Area + var defaultLocation = new Location(37.7749, -122.4194); + LoadMap(defaultLocation); + } + + /// + /// Loads the OpenStreetMap with Leaflet.js + /// + /// The center location for the map + private void LoadMap(Location centerLocation) + { + var html = GenerateMapHtml(centerLocation); + var htmlSource = new HtmlWebViewSource { Html = html }; + Source = htmlSource; + } + + /// + /// Generates the HTML content for the OpenStreetMap with Leaflet.js + /// + /// The center location for the map + /// HTML string containing the map + private string GenerateMapHtml(Location centerLocation) + { + return $@" + + + + + + OpenStreetMap + + + + +
+ + + +"; + } + + /// + /// Adds a location pin to the map + /// + /// The location to add + /// The label for the pin + /// Heat intensity (0.0 to 1.0) + public async Task AddLocationPinAsync(Location location, string label, double intensity = 0.5) + { + try + { + var script = $"addLocationPin({location.Latitude}, {location.Longitude}, '{label}', {intensity});"; + await EvaluateJavaScriptAsync(script); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Failed to add location pin to map"); + } + } + + /// + /// Adds a heat map polyline to the map + /// + /// List of coordinates for the polyline + /// Heat intensity (0.0 to 1.0) + public async Task AddHeatMapPolylineAsync(List coordinates, double intensity = 0.5) + { + try + { + var coordString = string.Join(",", coordinates.Select(c => $"[{c.Latitude}, {c.Longitude}]")); + var script = $"addHeatMapPolyline([{coordString}], {intensity});"; + await EvaluateJavaScriptAsync(script); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Failed to add heat map polyline to map"); + } + } + + /// + /// Clears all markers and polylines from the map + /// + public async Task ClearMapAsync() + { + try + { + await EvaluateJavaScriptAsync("clearMap();"); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Failed to clear map"); + } + } + + /// + /// Fits the map view to show all markers + /// + public async Task FitToBoundsAsync() + { + try + { + await EvaluateJavaScriptAsync("fitToBounds();"); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Failed to fit map to bounds"); + } + } + + /// + /// Updates the heat map visualization based on current location data. + /// + public async Task UpdateHeatMapAsync() + { + try + { + await ClearMapAsync(); + + if (!_locationData.Any()) + { + _logger?.LogInformation("No location data to display for heat map."); + return; + } + + // Group nearby points to determine intensity + var groupedLocations = GroupLocations(_locationData, 0.001); // Group within ~100 meters + + // Create heat map polylines and pins + foreach (var group in groupedLocations) + { + var intensity = Math.Min(1.0, (double)group.Count / 10.0); // Scale intensity + + if (group.Count > 1) + { + // Create polyline for grouped points + var coordinates = group.Select(loc => new Location(loc.Latitude, loc.Longitude)).ToList(); + await AddHeatMapPolylineAsync(coordinates, intensity); + } + + // Add a pin for each location (or group center) + var pinLocation = new Location(group.First().Latitude, group.First().Longitude); + await AddLocationPinAsync(pinLocation, $"Location {group.First().Id} (Intensity: {intensity:P0})", intensity); + } + + await FitToBoundsAsync(); + _logger?.LogInformation("Heat map updated with {Count} points.", _locationData.Count); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Failed to update heat map"); + } + } + + /// + /// Groups locations that are within a certain distance of each other. + /// + /// The list of all location data. + /// The maximum distance (in degrees) for grouping. + /// A list of grouped locations. + private static List> GroupLocations(List locations, double tolerance) + { + var grouped = new List>(); + var processed = new HashSet(); + + foreach (var loc in locations) + { + if (processed.Contains(loc)) continue; + + var currentGroup = new List { loc }; + processed.Add(loc); + + foreach (var otherLoc in locations) + { + if (loc == otherLoc || processed.Contains(otherLoc)) continue; + + var distance = Location.CalculateDistance( + new Location(loc.Latitude, loc.Longitude), + new Location(otherLoc.Latitude, otherLoc.Longitude), + DistanceUnits.Kilometers); + + if (distance < tolerance) // Roughly 100 meters + { + currentGroup.Add(otherLoc); + processed.Add(otherLoc); + } + } + grouped.Add(currentGroup); + } + return grouped; + } + + /// + /// Updates the visibility of heat map polylines. + /// + private void UpdateHeatMapVisibility() + { + // Implementation would require JavaScript communication + // For now, we'll handle this in the UpdateHeatMapAsync method + } + + /// + /// Updates the visibility of individual location pins. + /// + private void UpdatePointsVisibility() + { + // Implementation would require JavaScript communication + // For now, we'll handle this in the UpdateHeatMapAsync method + } + + /// + /// Loads location data and updates the heat map + /// + /// The location data to display + public async Task LoadLocationDataAsync(List locationData) + { + _locationData = locationData; + await UpdateHeatMapAsync(); + } +} diff --git a/LocationTrackerApp/Data/LocationDbContext.cs b/LocationTrackerApp/Data/LocationDbContext.cs new file mode 100644 index 0000000..6264071 --- /dev/null +++ b/LocationTrackerApp/Data/LocationDbContext.cs @@ -0,0 +1,143 @@ +using Microsoft.EntityFrameworkCore; +using LocationTrackerApp.Models; + +namespace LocationTrackerApp.Data; + +/// +/// Entity Framework DbContext for managing location data in SQLite database +/// +public class LocationDbContext : DbContext +{ + /// + /// Database path for SQLite storage + /// + public string DatabasePath { get; } + + /// + /// DbSet for LocationData entities + /// + public DbSet LocationData { get; set; } = null!; + + /// + /// Initializes a new instance of LocationDbContext + /// + /// Path to the SQLite database file + public LocationDbContext(string databasePath) + { + DatabasePath = databasePath; + } + + /// + /// Configures the database connection and options + /// + /// Options builder for configuring the database + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlite($"Data Source={DatabasePath}"); + + // Enable sensitive data logging for development (remove in production) + #if DEBUG + optionsBuilder.EnableSensitiveDataLogging(); + #endif + } + + /// + /// Configures entity relationships and constraints + /// + /// Model builder for configuring entities + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // Configure LocationData entity + modelBuilder.Entity(entity => + { + // Set primary key + entity.HasKey(e => e.Id); + + // Configure indexes for better query performance + entity.HasIndex(e => e.Timestamp) + .HasDatabaseName("IX_LocationData_Timestamp"); + + entity.HasIndex(e => e.SessionId) + .HasDatabaseName("IX_LocationData_SessionId"); + + entity.HasIndex(e => new { e.Latitude, e.Longitude }) + .HasDatabaseName("IX_LocationData_Coordinates"); + + // Configure column types and constraints + entity.Property(e => e.Latitude) + .HasColumnType("REAL") + .IsRequired(); + + entity.Property(e => e.Longitude) + .HasColumnType("REAL") + .IsRequired(); + + entity.Property(e => e.Accuracy) + .HasColumnType("REAL") + .HasDefaultValue(0.0); + + entity.Property(e => e.Altitude) + .HasColumnType("REAL"); + + entity.Property(e => e.Speed) + .HasColumnType("REAL"); + + entity.Property(e => e.Timestamp) + .HasColumnType("TEXT") + .IsRequired(); + + entity.Property(e => e.SessionId) + .HasColumnType("TEXT") + .HasMaxLength(100); + + entity.Property(e => e.Notes) + .HasColumnType("TEXT") + .HasMaxLength(500); + }); + } + + /// + /// Ensures the database is created and up to date + /// + public async Task EnsureDatabaseCreatedAsync() + { + await Database.EnsureCreatedAsync(); + } + + /// + /// Clears all location data from the database + /// + public async Task ClearAllLocationDataAsync() + { + await Database.ExecuteSqlRawAsync("DELETE FROM LocationData"); + } + + /// + /// Gets location data within a specified time range + /// + /// Start time for the query + /// End time for the query + /// List of location data within the time range + public async Task> GetLocationDataInTimeRangeAsync(DateTime startTime, DateTime endTime) + { + return await LocationData + .Where(l => l.Timestamp >= startTime && l.Timestamp <= endTime) + .OrderBy(l => l.Timestamp) + .ToListAsync(); + } + + /// + /// Gets location data for a specific session + /// + /// Session identifier + /// List of location data for the session + public async Task> GetLocationDataBySessionAsync(string sessionId) + { + return await LocationData + .Where(l => l.SessionId == sessionId) + .OrderBy(l => l.Timestamp) + .ToListAsync(); + } +} diff --git a/LocationTrackerApp/GlobalXmlns.cs b/LocationTrackerApp/GlobalXmlns.cs new file mode 100644 index 0000000..f661ca6 --- /dev/null +++ b/LocationTrackerApp/GlobalXmlns.cs @@ -0,0 +1,2 @@ +[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "LocationTrackerApp")] +[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "LocationTrackerApp.Pages")] diff --git a/LocationTrackerApp/LocationTrackerApp.csproj b/LocationTrackerApp/LocationTrackerApp.csproj new file mode 100644 index 0000000..9dd2c80 --- /dev/null +++ b/LocationTrackerApp/LocationTrackerApp.csproj @@ -0,0 +1,80 @@ + + + + net9.0-android;net9.0-ios;net9.0-maccatalyst + $(TargetFrameworks);net9.0-windows10.0.19041.0 + + + + + + + Exe + LocationTrackerApp + true + true + enable + enable + + + LocationTrackerApp + + + com.companyname.locationtrackerapp + + + 1.0 + 1 + + + None + + 15.0 + 15.0 + 21.0 + /Users/carlos/Library/Android/sdk + 34 + 34 + + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + diff --git a/LocationTrackerApp/MainPage.xaml b/LocationTrackerApp/MainPage.xaml new file mode 100644 index 0000000..1bb9295 --- /dev/null +++ b/LocationTrackerApp/MainPage.xaml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +