Many improvements to wasm and web frontend
This commit is contained in:
parent
795a333624
commit
025d0fcca3
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -30,6 +30,6 @@ placeholder="Enter address"></Svelecte>
|
||||
type="text"
|
||||
bind:value
|
||||
placeholder="Address"
|
||||
on:input
|
||||
on:change
|
||||
/>
|
||||
</div>
|
||||
|
@ -17,15 +17,14 @@
|
||||
export let value;
|
||||
</script>
|
||||
|
||||
<div class="mb-4">
|
||||
<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>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="text-gray-700 text-sm font-bold mb-2" for="username">{selectedCoordinateType}</label
|
||||
>
|
||||
</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"
|
||||
|
@ -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,
|
||||
|
@ -6,8 +6,9 @@
|
||||
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;
|
||||
@ -15,8 +16,9 @@
|
||||
|
||||
let map;
|
||||
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);
|
||||
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>
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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> {
|
||||
Self::try_from(
|
||||
xpin::Address::from_lat_lon(lat, lon)
|
||||
.as_ref()
|
||||
.map(EncodedAddress::from)
|
||||
.map_err(|e| e.to_string())
|
||||
.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)
|
||||
let mut ret = Self::try_from(
|
||||
xpin::Address::from_lat_lon(latlon.lat, latlon.lon)
|
||||
.as_ref()
|
||||
.map(EncodedAddress::from)
|
||||
.map_err(|e| e.to_string())?;
|
||||
.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> {
|
||||
Self::try_from(
|
||||
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]
|
||||
.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")
|
||||
// }
|
||||
// }
|
||||
|
Loading…
Reference in New Issue
Block a user