Skip to content

Commit

Permalink
39 move grid loading to an async helper function (#42)
Browse files Browse the repository at this point in the history
* move grid loading logic to grids module

* async register grid function

* use grid function in wrapper

* update changelog and geodesy commit link

* mark function UNSTABLE

* cargo
  • Loading branch information
Rennzie committed Nov 28, 2023
1 parent 9df6b27 commit 62a3f03
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 60 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Attribution for `OSTN15_NTv1_OSGBtoETRS.gsb` [#32](https://github.com/Rennzie/geodesy-wasm/issues/32)
- Support for 2D, 3D and 4D coordinates as tuples ([number, number]) or objects ({x, y, z, t}) to the wrapper
- Unstable support for loading a grid from the network
- Support for the `axisswap` operator via [Geodesy:#84](https://github.com/busstoptaktik/geodesy/pull/84)

### Changed

- Grids are stored in globally allocated heap memory [#34](https://github.com/Rennzie/geodesy-wasm/issues/34)
- Lazily initialise the operator so grids can be registered after creation
- Grids are loaded via standalone helper functions [#39](https://github.com/Rennzie/geodesy-wasm/issues/39)
- Renamed `noop` operator to `senmerc` in anticipation of adding a Sensat Mercator operator
- How Coordinates work [#19](https://github.com/Rennzie/geodesy-wasm/issues/19):
- Rename `CoordBuffer` to `Coordinates`
Expand Down
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[package]
name = "geodesy-wasm"
version = "0.5.0"
rust-version = "1.70"
keywords = ["geospatial", "geodesy", "cartography", "geography"]
categories = ["science"]
authors = ["Sean Rennie <sean.rennie@sensat.co.uk>"]
Expand All @@ -20,7 +21,7 @@ default = ["console_error_panic_hook", "console_log"]
[dependencies]
wasm-bindgen = "0.2.88"

geodesy_rs = { package = "geodesy", git = "https://github.com/Rennzie/geodesy.git", rev = "499f17952fa21a49a0e6a0a3d4cd35ab17c1bd1f", version = "0.11.0", features = [
geodesy_rs = { package = "geodesy", git = "https://github.com/Rennzie/geodesy.git", rev = "dd19f7f4ace656a04e009ca0133243cb49dc578c", version = "0.11.0", features = [
"js",
] }

Expand All @@ -34,7 +35,8 @@ thiserror = "1.0.44"
console_log = { version = "1.0.0", features = ["color"], optional = true }
log = "0.4.19"
float_eq = "1.0.1"
once_cell = "1.18.0"
wasm-bindgen-futures = "0.4.39"
reqwest = "0.11.22"

[dev-dependencies]
wasm-bindgen-test = "0.3.38"
Expand Down
8 changes: 4 additions & 4 deletions js/geodesy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as GeodesyWasm from '@geodesy-wasm';
import {Geo, registerGridSync} from '@geodesy-wasm';
import {Coordinates as WasmCoordinates} from '@geodesy-wasm';

export class Geodesy {
private ctx: GeodesyWasm.Geo;
private ctx: Geo;

/**
* The `Geodesy` class wraps the geodesy-wasm library and provides a simpler JS friendly interface that abstracts some of the wasmness out of it.
Expand All @@ -22,11 +22,11 @@ export class Geodesy {
* ```
*/
constructor(definition: string, gridMap?: Record<string, DataView>) {
this.ctx = new GeodesyWasm.Geo(tidyProjString(definition));
this.ctx = new Geo(tidyProjString(definition));

if (gridMap) {
for (const [key, value] of Object.entries(gridMap)) {
this.ctx.registerGrid(key, value);
registerGridSync(key, value);
}
}
// TODO: How do we cleanup wasm memory if the class is GC'd?
Expand Down
43 changes: 2 additions & 41 deletions src/geodesy/context.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
use super::{
coordinate::Coordinates,
wasmcontext::{WasmContext, GRIDS},
};
use super::{coordinate::Coordinates, wasmcontext::WasmContext};
use crate::error::{Error, WasmResult};
use geodesy_rs::{
authoring::{parse_proj, BaseGrid},
prelude::*,
Ntv2Grid,
};
use js_sys::{DataView, Uint8Array};
use std::sync::Arc;
use geodesy_rs::{authoring::*, parse_proj, OpHandle};
use wasm_bindgen::prelude::*;

/// A wrapper around a [geodesy_rs::Context]
Expand Down Expand Up @@ -96,34 +87,4 @@ impl Geo {
}
}
}

/// Register grids for use in the [Geo] class.
///
/// The keys used to load the grid MUST be the same
/// as the `grids=<key>` parameter in the definition string.
///
/// Supported Grid Types:
/// - `NTv2` (.gsb)
/// - `Gravsoft`
#[wasm_bindgen(js_name = registerGrid)]
pub fn register_grid(&self, key: &str, data_view: DataView) -> WasmResult<()> {
// IDEA: To get more sophisticated we could
// - fetch from the network by identifying if the name is http etc
// -- either from the cdn or from a user defined url
// - from IndexDB at a key/database that we pre-define

let grid: Vec<u8> = Uint8Array::new(&data_view.buffer()).to_vec();

let mut grids = GRIDS.lock().unwrap();

// TODO: Pull this into a separate function when we have more ways to get a grid
if key.trim().ends_with("gsb") {
grids.insert(key.to_string(), Arc::new(Ntv2Grid::new(&grid)?));
return Ok(());
} else {
grids.insert(key.to_string(), Arc::new(BaseGrid::gravsoft(&grid)?));
}

Ok(())
}
}
69 changes: 69 additions & 0 deletions src/geodesy/grids.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use crate::error::{Result, WasmResult};
use geodesy_rs::Grid;
use geodesy_rs::{authoring::BaseGrid, Ntv2Grid};
use js_sys::{DataView, Uint8Array};
use reqwest::Url;
use std::{
collections::BTreeMap,
sync::{Arc, Mutex, OnceLock},
};
use wasm_bindgen::prelude::*;

// A single store on the heap for all grids
pub static GRIDS: OnceLock<Mutex<BTreeMap<String, Arc<dyn Grid>>>> = OnceLock::new();

fn init_grids() -> Mutex<BTreeMap<String, Arc<dyn Grid>>> {
Mutex::new(BTreeMap::<String, Arc<dyn Grid>>::new())
}

/// A synchronous way to register grids for use in the [Geo] class with named [DataView]s.
///
/// The keys used to load the grid MUST be the same
/// as the `grids=<key>` parameter in the definition string.
///
/// Supported Grid Types:
/// - `NTv2` (.gsb)
/// - `Gravsoft`
#[wasm_bindgen(js_name = registerGridSync)]
pub fn register_grid_sync(key: &str, data_view: DataView) -> WasmResult<()> {
let grid: Vec<u8> = Uint8Array::new(&data_view.buffer()).to_vec();
add_grid(key, grid)?;

Ok(())
}

/// [Unstable] Add grids to the [Geo] class with names urls.
/// The URL MUST return a grid file in the supported formats.
///
/// The keys used to load the grid MUST be the same
/// as the `grids=<key>` parameter in the definition string.
///
/// Supported Grid Types:
/// - `NTv2` (.gsb)
/// - `Gravsoft`
#[wasm_bindgen(js_name = registerGrid)]
pub async fn UNSTABLE_register_grid(key: &str, url: &str) -> WasmResult<()> {
// TODO: Cache the result on IndexDB if available.
// - Make it possible to add headers when calling `register_grid`

let url = Url::parse(url)?;
let response = reqwest::get(url).await?;

let bytes = response.bytes().await?;
let byte_array = bytes.as_ref();

add_grid(key, byte_array.into())?;

Ok(())
}

fn add_grid(key: &str, grid_bytes: Vec<u8>) -> Result<()> {
let mut grids = GRIDS.get_or_init(init_grids).lock().unwrap();

if key.trim().ends_with("gsb") {
grids.insert(key.to_string(), Arc::new(Ntv2Grid::new(&grid_bytes)?));
} else {
grids.insert(key.to_string(), Arc::new(BaseGrid::gravsoft(&grid_bytes)?));
}
return Ok(());
}
1 change: 1 addition & 0 deletions src/geodesy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod context;
pub mod coordinate;
mod grids;
mod operators;
mod wasmcontext;
20 changes: 7 additions & 13 deletions src/geodesy/wasmcontext.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
use super::operators::ACCESSORY_OPERATORS;
use super::{grids::GRIDS, operators::ACCESSORY_OPERATORS};
use geodesy_rs::{authoring::*, Error as RgError};
use once_cell::sync::Lazy;
use std::{
collections::BTreeMap,
sync::{Arc, Mutex},
};

// A single store on the heap for all grids
pub static GRIDS: Lazy<Mutex<BTreeMap<String, Arc<dyn Grid>>>> =
Lazy::new(|| Mutex::new(BTreeMap::<String, Arc<dyn Grid>>::new()));
use std::{collections::BTreeMap, sync::Arc};

// ----- T H E W A S M C T X P R O V I D E R ---------------------------------
#[derive(Debug, Default)]
Expand Down Expand Up @@ -121,9 +113,11 @@ impl Context for WasmContext {

/// Access grid resources by identifier
fn get_grid(&self, name: &str) -> Result<Arc<(dyn Grid + 'static)>, RgError> {
if let Some(grid) = GRIDS.lock().unwrap().get(name) {
// It's a reference clone
return Ok(grid.clone());
if let Some(grids) = GRIDS.get() {
if let Some(grid) = grids.lock().unwrap().get(name) {
// It's a reference clone
return Ok(grid.clone());
}
}

Err(RgError::NotFound(
Expand Down

0 comments on commit 62a3f03

Please sign in to comment.