Many improvements to wasm and web frontend

This commit is contained in:
Austen Adler 2023-03-21 23:22:29 -04:00
parent 795a333624
commit 025d0fcca3
12 changed files with 175 additions and 136 deletions

11
Cargo.lock generated
View File

@ -226,6 +226,16 @@ dependencies = [
"vec_map",
]
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "cookie"
version = "0.16.2"
@ -2110,6 +2120,7 @@ dependencies = [
name = "xpin-wasm"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"spatial-coordinate-systems",
"wasm-bindgen",
"xpin",

View File

@ -23,7 +23,7 @@ impl Coordinate {
pub fn from(lat: f64, lon: f64) -> Option<Self> {
if (-90_f64..=90_f64).contains(&lat) && (-180_f64..=180_f64).contains(&lon) {
Some(Self { lat: lat, lon: lon })
Some(Self { lat, lon })
} else {
None
}

View File

@ -22,12 +22,14 @@ pub struct Coordinate {
impl Coordinate {
/// ```rust
/// use spatial_coordinate_systems::utm::Coordinate;
/// use spatial_coordinate_systems::LatLon;
///
/// assert!(Coordinate::parse("10S 706832mE 4344683mN").is_ok());
/// assert!(Coordinate::parse("10S 706832mE 4344683N").is_ok());
/// assert!(Coordinate::parse("10S 706832E 4344683N").is_ok());
/// assert!(Coordinate::parse("10S706832mE 4344683mN").is_err());
/// assert!(Coordinate::parse("10S 706832mE 4344683m").is_ok());
/// assert!(Coordinate::parse("34H 261877.8163738246 6243185.589276327").is_ok());
/// ```
pub fn parse(i: &str) -> IResult<&str, Self> {
map_opt(
@ -39,7 +41,7 @@ impl Coordinate {
parse_f64,
// TODO: Can there be spaces around the m here or no?
opt(complete::char('m')),
// TODO: Should I allow a direction here nor no
// TODO: Should I allow a direction here or not
opt(parse_direction),
space1,
parse_f64,
@ -112,7 +114,7 @@ impl TryFrom<LatLon> for Coordinate {
// TODO: This does not feel right
let zone_num = utm::lat_lon_to_zone_number(value.lat, value.lon);
let zone_letter = utm::lat_to_zone_letter(value.lat).ok_or(())?;
let (easting, northing, _) = utm::to_utm_wgs84(value.lat, value.lon, zone_num);
let (northing, easting, _) = utm::to_utm_wgs84(value.lat, value.lon, zone_num);
Ok(Self {
zone_num,
@ -122,3 +124,28 @@ impl TryFrom<LatLon> for Coordinate {
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_general() {
macro_rules! p {
($tt:tt) => {{
let cvt = Coordinate::from_str($tt);
eprintln!("Testing: {} => {:?}", $tt, cvt);
assert!(cvt.is_ok());
eprintln!("Now converting to latlon");
assert!(dbg!(TryInto::<LatLon>::try_into(cvt.unwrap())).is_ok());
}};
}
dbg!(dbg!(Coordinate::try_from(
LatLon::from(-33.92487_f64, 18.42406_f64).unwrap()
))
.unwrap()
.to_string());
p!("34H 261877.8163738246 6243185.589276327");
}
}

View File

@ -11,9 +11,7 @@ use crate::Error;
pub fn lat_lon_to_cellid(lat: f64, lon: f64) -> Result<CellID, Error> {
// Wrap the latitudes and longitudes
eprintln!("Pre-lat: {lat},{lon}");
let (lat, lon) = wrap::wrap_latlon(lat, lon);
eprintln!("Post-lat: {lat},{lon}");
if !lat.is_finite() || !lon.is_finite() {
return Err(Error::NaNLatLng);

View File

@ -291,6 +291,13 @@ mod tests {
}
}
#[test]
fn test_general() {
assert!(dbg!(Address::from_str("6532 BROADCAST TINY apple")).is_ok());
assert!(dbg!(Address::from_str("6532 BROADCAST TINY orange")).is_ok());
assert!(dbg!(Address::from_lat_lon(0.0, 0.0)).is_ok());
}
#[test]
fn test_parse_v0() {
// Regular case

View File

@ -30,6 +30,6 @@ placeholder="Enter address"></Svelecte>
type="text"
bind:value
placeholder="Address"
on:input
on:change
/>
</div>

View File

@ -17,15 +17,14 @@
export let value;
</script>
<select bind:value={selectedCoordinateType} on:change={selectedCoordinateTypeChange}>
{#each coordinateTypes as t}
<option value={t}>{t}</option>
{/each}
</select>
<div class="mb-4">
<label class="text-gray-700 text-sm font-bold mb-2" for="username">{selectedCoordinateType}</label
>
<label class="text-gray-700 text-sm font-bold mb-2" for="username">
<select bind:value={selectedCoordinateType} on:change={selectedCoordinateTypeChange}>
{#each coordinateTypes as t}
<option value={t}>{t}</option>
{/each}
</select>
</label>
<input
class="shadow appearance-none border rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline w-full"
type="text"

View File

@ -8,21 +8,6 @@ export const emptyxpin = {
srcCoordsRepr: '0.0, 0.0'
};
export function getxpin(xpin) {
if (!xpin) {
return;
}
return {
address: xpin.get_address(),
latLon: xpin.get_lat_lon(),
decimalDegrees: xpin.get_decimal_degrees(),
srcCoordsType: xpin.get_coords_repr_type(),
srcCoordsRepr: xpin.get_coords_repr(),
xpin: xpin
};
}
export const WasmStatus = {
NotLoaded: -1,
Loaded: 0,

View File

@ -6,17 +6,19 @@
import CoordinateInput from '$lib/CoordinateInput.svelte';
import { browser } from '$app/environment';
import Error from './Error.svelte';
import { emptyxpin, getxpin } from '$lib/common.js';
import { emptyxpin } from '$lib/common.js';
let initSuccess = false;
let coordinateTypes = [];
let selectedCoordinateType;
let leaflet;
import Map from '$lib/Map.svelte';
let map;
let coordinateInputValue = '0, 0';
let coordinateInputValue = '0,0';
// let coordinateInputValue = '16U 5737178.365943674 437486.0153131335';
let addr = emptyxpin;
let addr;
let addrInputValue = '';
let wasm = {
@ -25,41 +27,34 @@
call: undefined
};
const updateEditedAddress = (latlng) => {
let popup = leaflet.popup();
try {
addr = getxpin(wasm.call.EncodedAddress.from_lat_lon(latlng.lat, latlng.lng));
addrInputValue = addr.address;
coordinateInputValue = addr.decimalDegrees;
popup
.setLatLng({
lat: addr.latLon[0],
lng: addr.latLon[1]
})
.setContent(`${addr.address}`)
.openOn(map);
} catch (err) {
console.error(err);
addrInputValue = '';
coordinateInputValue = '0, 0';
popup.setLatLng(latlng).setContent(`You clicked at ${latlng}`).openOn(map);
}
};
// const updateEditedAddress = (latlng) => {
// let popup = leaflet.popup();
// try {
// addr = wasm.call.EncodedAddress.from_lat_lon(latlng.lat, latlng.lng);
// addrInputValue = addr.address;
// coordinateInputValue = addr.decimalDegrees;
// popup
// .setLatLng({
// lat: addr.latLon[0],
// lng: addr.latLon[1]
// })
// .setContent(`${addr.address}`)
// .openOn(map);
// } catch (err) {
// console.error(err);
// addrInputValue = '';
// coordinateInputValue = '0, 0';
// popup.setLatLng(latlng).setContent(`You clicked at ${latlng}`).openOn(map);
// }
// };
const locationfound = (e) => {
console.log('Updating current location event', e);
// kupdateEditedAddress(e.detail.latlng);
updateAddr(
getxpin(wasm.call.EncodedAddress.from_coordinate(`${e.latlng.lat}, ${e.latlng.lng}`)),
false
);
updateAddr(wasm.call.EncodedAddress.from_coordinate(`${e.latlng.lat}, ${e.latlng.lng}`), false);
};
const onMapClick = (e) => {
updateAddr(
getxpin(wasm.call.EncodedAddress.from_coordinate(`${e.latlng.lat}, ${e.latlng.lng}`)),
false
);
updateAddr(wasm.call.EncodedAddress.from_coordinate(`${e.latlng.lat}, ${e.latlng.lng}`), false);
};
let init = async () => {
@ -79,6 +74,13 @@
.then(async () => {
leaflet = await import('leaflet');
})
.then(async () => {
// Initialize the app
updateAddr(wasm.call.EncodedAddress.from_coordinate(coordinateInputValue), true);
})
.then(async () => {
initSuccess = true;
})
.catch((err) => {
console.log('Erroring');
console.error(err);
@ -95,19 +97,20 @@
let outputValue = ' ';
const selectedCoordinateTypeChange = () => {
console.log('New type:', selectedCoordinateType);
coordinateInputValue = addr.xpin.get_coords_repr_as(
coordinateTypes.indexOf(selectedCoordinateType)
);
// Update everything to handle the case where the address is currently invalid
updateAddr(wasm.call.EncodedAddress.from_coordinate(coordinateInputValue), true);
coordinateInputValue = addr.get_coords_repr_as(coordinateTypes.indexOf(selectedCoordinateType));
};
const coordinateInput = () => {
try {
let parsed = wasm.call.EncodedAddress.from_coordinate(coordinateInputValue);
let xpin = wasm.call.EncodedAddress.from_coordinate(coordinateInputValue);
updateAddr(getxpin(parsed), true);
updateAddr(xpin, true);
console.log('parsed:', parsed);
console.log('xpin:', xpin);
selectedCoordinateType = coordinateTypes[xpin.srcCoordsType] || selectedCoordinateType;
} catch (e) {
console.error('Could not parse coordinate input:', e);
}
@ -121,7 +124,9 @@
let latlng = new leaflet.LatLng(addr.latLon[0], addr.latLon[1]);
map.panTo(latlng, 20);
leaflet.popup().setLatLng(latlng).setContent(`${addr.address}`).openOn(map);
map.setView(latlng);
if (fromTextInput) {
map.setView(latlng);
}
outputValue = ' ';
addrInputValue = addr.address;
@ -130,13 +135,11 @@
if (fromTextInput) {
selectedCoordinateType = coordinateTypes[addr.srcCoordsType] || selectedCoordinateType;
coordinateInputValue = addr.srcCoordsRepr || coordinateInputValue;
console.log('hi');
} else {
console.log('Getting it in the format of', selectedCoordinateType);
coordinateInputValue = addr.xpin.get_coords_repr_as(
coordinateInputValue = addr.get_coords_repr_as(
coordinateTypes.indexOf(selectedCoordinateType)
);
console.log('Result:', coordinateInputValue);
}
}
} catch (e) {
@ -147,7 +150,7 @@
const addressInput = () => {
try {
updateAddr(getxpin(wasm.call.EncodedAddress.from_address(addrInputValue)), false);
updateAddr(wasm.call.EncodedAddress.from_address(addrInputValue), false);
} catch (e) {
console.error(e);
outputValue = `${e}`;
@ -174,7 +177,7 @@
<p>Address: <span class="font-bold">{addr.address}</span></p>
<p class="text-sm">({addr.latLon}) => ({addr.latLon})</p>
<AddressInput bind:value={addrInputValue} on:input={addressInput} />
<AddressInput bind:value={addrInputValue} on:change={addressInput} />
<CoordinateInput
bind:value={coordinateInputValue}
@ -183,8 +186,10 @@
bind:coordinateTypes
bind:selectedCoordinateType
/>
<Map bind:map {onMapClick} on:locationfound={locationfound} />
{:catch message}
<Error {message}>Could not start core module</Error>
{/await}
<span class:invisible={!initSuccess}>
<Map bind:map {onMapClick} on:locationfound={locationfound} />
</span>

View File

@ -184,7 +184,7 @@ mod tests {
assert_ne!(w2e, w3e);
assert_ne!(w1e, w3e);
// Make sure the encoded owrds are not the same
// Make sure the encoded words are not the same
assert_ne!(w1e.number, w2e.number);
assert_ne!(w2e.number, w3e.number);
assert_ne!(w1e.number, w3e.number);

View File

@ -12,3 +12,4 @@ crate-type = ["cdylib"]
wasm-bindgen = "0.2"
xpin = {path=".."}
spatial-coordinate-systems = {path="../spatial-coordinate-systems/"}
console_error_panic_hook = "0.1.7"

View File

@ -41,61 +41,52 @@ impl From<spatial_coordinate_systems::CoordinateType> for CoordinateType {
}
}
#[wasm_bindgen]
#[wasm_bindgen(getter_with_clone)]
pub struct EncodedAddress {
address: String,
pub address: String,
/// The coordinates used to encode this address
src_coords: Option<Coordinate>,
// coords_repr: Option<String>,
pub lat: f64,
pub lon: f64,
src_coords: Coordinate,
#[wasm_bindgen(js_name = srcCoordsRepr)]
pub src_coords_repr: String,
#[wasm_bindgen(js_name = srcCoordsType)]
pub src_coords_type: CoordinateType,
#[wasm_bindgen(js_name = latLon)]
pub lat_lon: Box<[f64]>,
#[wasm_bindgen(js_name = decimalDegrees)]
pub decimal_degrees: String,
}
#[wasm_bindgen]
impl EncodedAddress {
/// Get the current address as decimal degrees
#[wasm_bindgen]
pub fn get_decimal_degrees(&self) -> String {
format!("{}, {}", self.lat, self.lon)
}
pub fn get_coords_repr_type(&self) -> Option<CoordinateType> {
self.src_coords
.as_ref()
.map(|c| c.get_type())
.map(From::from)
}
/// Get the string representation of the encoded value
#[wasm_bindgen]
pub fn get_coords_repr(&self) -> Option<String> {
self.src_coords.as_ref().map(|s| s.to_string())
}
/// Get the string representation of the encoded value
#[wasm_bindgen]
// TODO: Do not return option
pub fn get_coords_repr_as(&self, coordinate_type: CoordinateType) -> Option<String> {
self.src_coords
.clone()
.as_ref()
// TODO: Remove the clone here
.map(|c| c.clone().as_type(&coordinate_type.into()).ok())
.flatten()
.clone()
.as_type(&coordinate_type.into())
.ok()
.map(|s| s.to_string())
}
/// Get an encoded address from a latitude/longitude
#[wasm_bindgen]
pub fn from_lat_lon(lat: f64, lon: f64) -> Result<EncodedAddress, String> {
xpin::Address::from_lat_lon(lat, lon)
.as_ref()
.map(EncodedAddress::from)
.map_err(|e| e.to_string())
Self::try_from(
xpin::Address::from_lat_lon(lat, lon)
.as_ref()
.map_err(|e| e.to_string())?,
)
.map_err(|()| String::from("Could not convert xpin to address"))
}
/// Get an encoded address from a latitude/longitude
#[wasm_bindgen]
pub fn from_coordinate(i: &str) -> Result<EncodedAddress, String> {
console_error_panic_hook::set_once();
let src_coords = Coordinate::from_str(i)
.map_err(|()| format!("Could not parse str as a coordinate {i:?}"))?;
@ -103,43 +94,58 @@ impl EncodedAddress {
let latlon = LatLon::try_from(src_coords.clone())
.map_err(|_| format!("Could not convert coordinate back to latlon"))?;
let mut ret = xpin::Address::from_lat_lon(latlon.lat, latlon.lon)
.as_ref()
.map(EncodedAddress::from)
.map_err(|e| e.to_string())?;
let mut ret = Self::try_from(
xpin::Address::from_lat_lon(latlon.lat, latlon.lon)
.as_ref()
.map_err(|e| e.to_string())?,
)
.map_err(|()| String::from("Could not convert xpin to address"))?;
ret.src_coords = Some(src_coords);
ret.src_coords_repr = src_coords.to_string();
ret.src_coords_type = src_coords.get_type().into();
ret.src_coords = src_coords;
Ok(ret)
}
#[wasm_bindgen]
pub fn from_address(addr_str: &str) -> Result<EncodedAddress, String> {
xpin::Address::from_str(addr_str)
.as_ref()
.map(EncodedAddress::from)
.map_err(|e| e.to_string())
}
#[wasm_bindgen]
pub fn get_address(&self) -> String {
self.address.clone()
}
#[wasm_bindgen]
pub fn get_lat_lon(&self) -> Vec<f64> {
vec![self.lat, self.lon]
Self::try_from(
xpin::Address::from_str(addr_str)
.as_ref()
.map_err(|e| e.to_string())?,
)
.map_err(|()| String::from("Could not convert xpin to address"))
}
}
impl From<&'_ Address<'_>> for EncodedAddress {
fn from(addr: &Address) -> Self {
impl TryFrom<&'_ Address<'_>> for EncodedAddress {
type Error = ();
fn try_from(addr: &Address) -> Result<Self, Self::Error> {
console_error_panic_hook::set_once();
let (lat, lon) = addr.as_lat_lon();
Self {
let src_coords = Coordinate::from_str(&format!("{}, {}", lat, lon))?;
Ok(Self {
address: addr.to_string(),
src_coords: None,
lat,
lon,
}
// TODO: Do not use formatting here
lat_lon: Box::new([lat, lon]),
src_coords_repr: src_coords.to_string(),
src_coords_type: src_coords.get_type().into(),
src_coords,
decimal_degrees: format!("{}, {}", lat, lon),
})
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// #[test]
// fn test_general() {
// super::from_address("6532 BROADCAST TINY apple")
// }
// }