mirror of
https://github.com/seemueller-io/yachtpit.git
synced 2025-09-08 22:46:45 +00:00
Integrate browser geolocation API (#9)
* Add GPS service and nautical base city data - Implement `GpsService` with methods for position updates and enabling/disabling GPS. - Introduce test data for nautical base cities with key attributes like population, coordinates, and images. - Update dependencies in `bun.lock` with required packages such as `geojson`. * give map a custom style * shift towards rust exclusivity * `build.rs` streamlines map build. Added an axum server with the map assets embedded. * update readmes * base-map api retrieves geolocation from the navigator of the browser * make map standalone wry that pulls assets from the server to simulate behavior in bevy * wip wasm * wasm build fixed * fix path ref to assets --------- Co-authored-by: geoffsee <>
This commit is contained in:
@@ -21,3 +21,18 @@ datalink = { path = "../datalink" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
datalink-provider = { path = "../datalink-provider" }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-bindgen = { workspace = true }
|
||||
web-sys = { version = "0.3.77", features = [
|
||||
"console",
|
||||
"Geolocation",
|
||||
"Navigator",
|
||||
"Window",
|
||||
"Document",
|
||||
"Element",
|
||||
"Position",
|
||||
"PositionOptions",
|
||||
"PositionError",
|
||||
"Coordinates"
|
||||
] }
|
115
crates/systems/src/geo_plugin.rs
Normal file
115
crates/systems/src/geo_plugin.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
// src/geo_plugin.rs
|
||||
use bevy::prelude::*;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::window;
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct UserLocation {
|
||||
pub lat: f64,
|
||||
pub lon: f64,
|
||||
pub accuracy: f64,
|
||||
pub fresh: bool,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct LocationData {
|
||||
pub data: Arc<Mutex<Option<(f64, f64, f64)>>>,
|
||||
}
|
||||
|
||||
impl Default for LocationData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GeoPlugin;
|
||||
impl Plugin for GeoPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<UserLocation>()
|
||||
.init_resource::<LocationData>();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
app.add_systems(Startup, request_location)
|
||||
.add_systems(Update, update_location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn request_location(location_data: Res<LocationData>) {
|
||||
let window = match window() {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
warn!("No window object available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let geo = match window.navigator().geolocation() {
|
||||
Ok(g) => g,
|
||||
Err(_) => {
|
||||
warn!("Geolocation not available");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let data_clone = location_data.data.clone();
|
||||
|
||||
let success = Closure::<dyn FnMut(web_sys::Position)>::new(move |pos: web_sys::Position| {
|
||||
let c: web_sys::Coordinates = pos.coords();
|
||||
if let Ok(mut data) = data_clone.lock() {
|
||||
*data = Some((c.latitude(), c.longitude(), c.accuracy()));
|
||||
}
|
||||
});
|
||||
|
||||
let error = Closure::<dyn FnMut(web_sys::PositionError)>::new(move |err: web_sys::PositionError| {
|
||||
match err.code() {
|
||||
1 => {
|
||||
warn!("Geolocation permission denied. This may be due to:");
|
||||
warn!(" - User denied location access");
|
||||
warn!(" - Insecure connection (HTTP instead of HTTPS)");
|
||||
warn!(" - Browser security settings");
|
||||
warn!(" Consider serving over HTTPS for geolocation access");
|
||||
},
|
||||
2 => {
|
||||
warn!("Geolocation position unavailable: {}", err.message());
|
||||
},
|
||||
3 => {
|
||||
warn!("Geolocation timeout: {}", err.message());
|
||||
},
|
||||
_ => {
|
||||
warn!("Geolocation error: {} (code: {})", err.message(), err.code());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// watch_position keeps updating; get_current_position is one‑shot
|
||||
match geo.watch_position_with_error_callback(success.as_ref().unchecked_ref(), Some(error.as_ref().unchecked_ref())) {
|
||||
Ok(_) => {
|
||||
success.forget(); // leak the closure so it lives forever
|
||||
error.forget(); // leak the error closure too
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to start watching position: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn update_location(mut loc: ResMut<UserLocation>, location_data: Res<LocationData>) {
|
||||
if let Ok(mut data) = location_data.data.lock() {
|
||||
if let Some((lat, lon, accuracy)) = data.take() {
|
||||
loc.lat = lat;
|
||||
loc.lon = lon;
|
||||
loc.accuracy = accuracy;
|
||||
loc.fresh = true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ mod vessel;
|
||||
mod ais;
|
||||
mod gps;
|
||||
mod radar;
|
||||
mod geo_plugin;
|
||||
|
||||
// Re-export components from the components crate
|
||||
pub use components::{
|
||||
@@ -16,3 +17,5 @@ pub use components::{
|
||||
|
||||
pub use world::player::{get_vessel_systems, setup_instrument_cluster_system, PlayerPlugin};
|
||||
pub use vessel::vessel_systems::{create_vessel_systems, AisSystem, GpsSystem, RadarSystem, SystemInteraction, SystemStatus, VesselSystem};
|
||||
|
||||
pub use geo_plugin::GeoPlugin;
|
Reference in New Issue
Block a user