From db921575207eb9fd286a2c4f4058e7a2c7383666 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 29 Dec 2023 15:55:27 +0900 Subject: [PATCH] Manually save state --- backend/src/lib.rs | 9 +++++++-- backend/src/map_model.rs | 31 ++++++++++++++++++++++++++++--- web/src/App.svelte | 12 ++++++------ web/src/MapLoader.svelte | 13 +++++++++++-- web/src/common/index.ts | 12 ++++++++++++ 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 6342c44..dd0e46a 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -133,9 +133,7 @@ impl LTN { /// Takes a LineString feature #[wasm_bindgen(js_name = addManyModalFilters)] pub fn add_many_modal_filters(&mut self, input: JsValue) -> Result { - info!("parse it"); let gj: Feature = serde_wasm_bindgen::from_value(input)?; - info!("make it geo"); let mut linestring: LineString = gj.try_into().map_err(err_to_js)?; self.map.mercator.to_mercator_in_place(&mut linestring); @@ -176,6 +174,13 @@ impl LTN { )) .map_err(err_to_js)?) } + + /// GJ with modal filters and optionally the neighbourhood boundary + #[wasm_bindgen(js_name = toSavefile)] + pub fn to_savefile(&self) -> Result { + // TODO Trim coordinates... in mercator? + Ok(serde_json::to_string(&self.map.to_savefile(self.neighbourhood.as_ref())).map_err(err_to_js)?) + } } #[derive(Deserialize)] diff --git a/backend/src/map_model.rs b/backend/src/map_model.rs index 240ca83..e4ff123 100644 --- a/backend/src/map_model.rs +++ b/backend/src/map_model.rs @@ -3,13 +3,13 @@ use std::fmt; use anyhow::Result; use geo::{ - Closest, ClosestPoint, Coord, EuclideanLength, Intersects, Line, LineIntersection, + LineInterpolatePoint, Closest, ClosestPoint, Coord, EuclideanLength, Intersects, Line, LineIntersection, LineLocatePoint, LineString, Point, }; -use geojson::{Feature, Geometry}; +use geojson::{Feature, Geometry, GeoJson}; use serde::Serialize; -use crate::{Mercator, Tags}; +use crate::{Mercator, Tags, Neighbourhood}; pub struct MapModel { pub roads: Vec, @@ -173,6 +173,31 @@ impl MapModel { let cmd = self.do_edit(cmd); self.undo_stack.push(cmd); } + + pub fn to_savefile(&self, neighbourhood: Option<&Neighbourhood>) -> GeoJson { + let mut features = Vec::new(); + + // A point per modal filter + // (When we detect existing, maybe need to instead compact edits) + for (r, modal_filter) in &self.modal_filters { + let pt = self + .get_r(*r) + .linestring + .line_interpolate_point(modal_filter.percent_along) + .unwrap(); + let mut f = Feature::from(Geometry::from(&self.mercator.to_wgs84(&pt))); + f.set_property("kind", "modal_filter"); + features.push(f); + } + + if let Some(neighbourhood) = neighbourhood { + let mut f = Feature::from(Geometry::from(&self.mercator.to_wgs84(&neighbourhood.boundary_polygon))); + f.set_property("kind", "boundary"); + features.push(f); + } + + GeoJson::from(features) + } } impl Road { diff --git a/web/src/App.svelte b/web/src/App.svelte index 1b05e1e..a511cf2 100644 --- a/web/src/App.svelte +++ b/web/src/App.svelte @@ -47,11 +47,9 @@ let map: Map; function zoomToFit() { - if (map && app) { - // TODO wasteful - let bbox = turfBbox(JSON.parse(app.render())); - map.fitBounds(bbox, { animate: false }); - } + // TODO wasteful + let bbox = turfBbox(JSON.parse(app.render())); + map.fitBounds(bbox, { animate: false }); } function gotApp(_x: LTN) { @@ -83,8 +81,10 @@
{#if map} + {#if app} +
+ {/if} {/if} -
@@ -117,4 +122,8 @@ on:loading={(e) => (msg = e.detail)} on:error={(e) => window.alert(e.detail)} /> + + {#if app} +
+ {/if}
diff --git a/web/src/common/index.ts b/web/src/common/index.ts index ee482c8..92111e4 100644 --- a/web/src/common/index.ts +++ b/web/src/common/index.ts @@ -56,3 +56,15 @@ export function makeColorRamp( step.push(colorScale[colorScale.length - 1]); return step as DataDrivenPropertyValueSpecification; } + +export function downloadGeneratedFile(filename: string, textInput: string) { + let element = document.createElement("a"); + element.setAttribute( + "href", + "data:text/plain;charset=utf-8, " + encodeURIComponent(textInput) + ); + element.setAttribute("download", filename); + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +}