Skip to content

Commit

Permalink
Color lines to show freehand/snapped as well
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Oct 26, 2023
1 parent 5ea34fe commit 72a4379
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ changes.
## Unreleased

- Always start in snap mode for a new route, even if the user last used freehand mode.
- Color lines to show freehand/snapped as well

## 0.2.2

Expand Down
4 changes: 2 additions & 2 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
filter: ["in", "$type", "LineString"],
type: "line",
paint: {
"line-color": "blue",
"line-color": "green",
"line-width": 5,
},
});
Expand All @@ -168,7 +168,7 @@
filter: ["in", "$type", "Polygon"],
type: "fill",
paint: {
"fill-color": "blue",
"fill-color": "green",
"fill-opacity": 0.2,
},
});
Expand Down
8 changes: 4 additions & 4 deletions route-snapper/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class RouteSnapper {
"line-join": "round",
},
paint: {
"line-color": "black",
"line-color": ["case", ["get", "snapped"], "red", "blue"],
"line-width": 2.5,
},
});
Expand Down Expand Up @@ -272,7 +272,7 @@ export class RouteSnapper {
</label>
</div>
<div id="snap_mode" style="background: red;">
<div id="snap_mode" style="background: red; color: white; padding: 8px">
Snapping to transport network
</div>
Expand Down Expand Up @@ -360,10 +360,10 @@ export class RouteSnapper {
return;
}
if (gj.snap_mode) {
snapDiv.style = "background: red";
snapDiv.style = "background: red; color: white; padding: 8px";
snapDiv.innerHtml = "Snapping to transport network";
} else {
snapDiv.style = "background: blue";
snapDiv.style = "background: blue; color: white; padding: 8px";
snapDiv.innerHtml = "Drawing freehand points";
}
}
Expand Down
86 changes: 72 additions & 14 deletions route-snapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,7 @@ impl JsRouteSnapper {
let mut draw_circles = BTreeMap::new();

// Draw the confirmed route
if let Some(pl) = self.entire_line_string() {
let geometry = pl.to_geojson(Some(&self.router.map.gps_bounds));
result.push((geometry, serde_json::Map::new()));
}
result.extend(self.line_string_broken_down());
for entry in &self.route.full_path {
// Every free point is a waypoint, so just handle it below
if let PathEntry::SnappedPoint(node) = entry {
Expand Down Expand Up @@ -306,9 +303,11 @@ impl JsRouteSnapper {
&self.router.map,
dir_edge,
));
let mut props = serde_json::Map::new();
props.insert("snapped".to_string(), true.into());
result.push((
pl.to_geojson(Some(&self.router.map.gps_bounds)),
serde_json::Map::new(),
props,
));
}
}
Expand All @@ -318,19 +317,20 @@ impl JsRouteSnapper {
self.router.map.node(*last),
self.router.map.node(current),
]);
let mut props = serde_json::Map::new();
props.insert("snapped".to_string(), false.into());
result.push((
pl.to_geojson(Some(&self.router.map.gps_bounds)),
serde_json::Map::new(),
props,
));
}
}
Waypoint::Free(pt) => {
let pl =
PolyLine::unchecked_new(vec![*pt, self.router.map.node(current)]);
result.push((
pl.to_geojson(Some(&self.router.map.gps_bounds)),
serde_json::Map::new(),
));
let mut props = serde_json::Map::new();
props.insert("snapped".to_string(), false.into());
result.push((pl.to_geojson(Some(&self.router.map.gps_bounds)), props));
}
}
}
Expand All @@ -348,10 +348,9 @@ impl JsRouteSnapper {
Waypoint::Free(pt) => pt,
};
let pl = PolyLine::unchecked_new(vec![last_pt, pt]);
result.push((
pl.to_geojson(Some(&self.router.map.gps_bounds)),
serde_json::Map::new(),
));
let mut props = serde_json::Map::new();
props.insert("snapped".to_string(), false.into());
result.push((pl.to_geojson(Some(&self.router.map.gps_bounds)), props));
}
}

Expand Down Expand Up @@ -744,6 +743,65 @@ impl JsRouteSnapper {
Some(PolyLine::unchecked_new(pts))
}

// Returns the entire_line_string, but broken into pieces with a snapped=true/false property.
fn line_string_broken_down(
&self,
) -> Vec<(
geojson::Geometry,
serde_json::Map<String, serde_json::Value>,
)> {
let mut result = Vec::new();
if self.route.full_path.is_empty() {
return result;
}
let mut add_result = |mut pts: Vec<Pt2D>, snapped: bool| {
pts.dedup();
if pts.len() >= 2 {
let geometry =
PolyLine::unchecked_new(pts).to_geojson(Some(&self.router.map.gps_bounds));
let mut props = serde_json::Map::new();
props.insert("snapped".to_string(), snapped.into());
result.push((geometry, props));
}
};

let mut prev_snapped = !matches!(self.route.full_path[0], PathEntry::FreePoint(_));
let mut pts = Vec::new();

for entry in &self.route.full_path {
let pt = match entry {
PathEntry::SnappedPoint(node) => self.router.map.node(*node),
PathEntry::FreePoint(pt) => *pt,
PathEntry::Edge(dir_edge) => {
pts.extend(edge_geometry(&self.router.map, *dir_edge));
continue;
}
};
let snapped = !matches!(entry, PathEntry::FreePoint(_));

if prev_snapped == snapped {
pts.push(pt);
} else if prev_snapped {
// Starting freehand
let last_pt = *pts.last().unwrap();
add_result(std::mem::take(&mut pts), true);
prev_snapped = false;
pts = vec![last_pt, pt];
} else {
// Starting snapped
pts.push(pt);
add_result(std::mem::take(&mut pts), false);
prev_snapped = true;
pts = vec![pt];
}
}

// Handle the last transition
add_result(std::mem::take(&mut pts), prev_snapped);

result
}

fn into_polygon_area(&self) -> Option<geojson::Geometry> {
if !self.route.is_closed_area() {
return None;
Expand Down
2 changes: 1 addition & 1 deletion user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ There are a few methods on the `RouteSnapper` object you can call:
If you're using the WASM API directly, the best reference is currently [the code](https://github.com/dabreegster/route_snapper/blob/main/route-snapper/src/lib.rs). Some particulars:

- `renderGeojson` returns a GeoJSON FeatureCollection to render the current state of the tool.
- It'll include a LineString showing the confirmed route and also any speculative addition, based on the current state.
- It'll include LineStrings showing the confirmed route and also any speculative addition, based on the current state. The LineStrings will have a boolean `snapped` property, which is false if either end touches a freehand point.
- In area mode, it'll have a Polygon once there are at least 3 points.
- It'll include a Point for every graph node involved in the current route. These will have a `type` property that's either `snapped-waypoint`, `free-waypoint`, or just `node` to indicate a draggable node that hasn't been touched yet. One Point may also have a `"hovered": true` property to indicate the mouse is currently on that Point.
- The GeoJSON object will also contain a foreign member called `cursor` to indicate the current mode of the tool. The values can be set to `map.getCanvas().style.cursor` as desired.
Expand Down

0 comments on commit 72a4379

Please sign in to comment.