Skip to content

Commit

Permalink
Fix issues on removing/adding images to a render bundle
Browse files Browse the repository at this point in the history
* Add `feature_highlight` example
  • Loading branch information
lennart authored Jun 19, 2024
1 parent d9d4368 commit b6fce29
Show file tree
Hide file tree
Showing 6 changed files with 421 additions and 118 deletions.
18 changes: 18 additions & 0 deletions galileo/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,24 @@ You can generate an image yourself running this example

- Same as the raster tiles example, but with support for [egui](https://www.egui.rs/).

</td>
</tr>
<tr>
<td>

[highlight_features](./highlight_features.rs)

</td>
<td>

![i](https://maximkaaa.github.io/galileo/highlight_features.png)

</td>
<td>

- Get and update features on cursor hover
- Show a different pin image based on feature state

</td>
</tr>
</tbody>
Expand Down
Binary file added galileo/examples/data/pin-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
181 changes: 181 additions & 0 deletions galileo/examples/highlight_features.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
use std::sync::{Arc, RwLock};

use galileo::control::{EventPropagation, UserEvent};
use galileo::decoded_image::DecodedImage;
use galileo::layer::feature_layer::symbol::Symbol;
use galileo::layer::feature_layer::{Feature, FeatureLayer};
use galileo::render::point_paint::PointPaint;
use galileo::render::render_bundle::RenderPrimitive;
use galileo::tile_scheme::TileSchema;
use galileo::MapBuilder;
use galileo_types::cartesian::{CartesianPoint3d, Point2d};
use galileo_types::geo::{Crs, Projection};
use galileo_types::geometry::Geom;
use galileo_types::impls::{Contour, Polygon};
use galileo_types::{latlon, CartesianGeometry2d, Geometry};
use lazy_static::lazy_static;
use nalgebra::Vector2;
use num_traits::AsPrimitive;

const YELLOW_PIN: &[u8] = include_bytes!("data/pin-yellow.png");
const GREEN_PIN: &[u8] = include_bytes!("data/pin-green.png");

lazy_static! {
static ref YELLOW_PIN_IMAGE: Arc<DecodedImage> =
Arc::new(DecodedImage::new(YELLOW_PIN).expect("Must have Yellow Pin Image"));
static ref GREEN_PIN_IMAGE: Arc<DecodedImage> =
Arc::new(DecodedImage::new(GREEN_PIN).expect("Must have Green Pin Image"));
}

#[tokio::main]
async fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
run(MapBuilder::new()).await;
}

#[derive(Debug, PartialEq, Default)]
pub(crate) struct PointMarker {
pub(crate) point: Point2d,
pub(crate) highlighted: bool,
}

impl Feature for PointMarker {
type Geom = Self;

fn geometry(&self) -> &Self::Geom {
self
}
}

impl Geometry for PointMarker {
type Point = Point2d;

fn project<P: Projection<InPoint = Self::Point> + ?Sized>(
&self,
projection: &P,
) -> Option<Geom<P::OutPoint>> {
self.point.project(projection)
}
}

impl CartesianGeometry2d<Point2d> for PointMarker {
fn is_point_inside<
Other: galileo_types::cartesian::CartesianPoint2d<
Num = <Point2d as galileo_types::cartesian::CartesianPoint2d>::Num,
>,
>(
&self,
point: &Other,
tolerance: <Point2d as galileo_types::cartesian::CartesianPoint2d>::Num,
) -> bool {
self.point.is_point_inside(point, tolerance)
}

fn bounding_rectangle(
&self,
) -> Option<
galileo_types::cartesian::Rect<
<Point2d as galileo_types::cartesian::CartesianPoint2d>::Num,
>,
> {
None
}
}

struct ColoredPointSymbol {}

pub async fn run(builder: MapBuilder) {
#[cfg(not(target_arch = "wasm32"))]
let builder = builder.with_raster_tiles(
|index| {
format!(
"https://tile.openstreetmap.org/{}/{}/{}.png",
index.z, index.x, index.y
)
},
TileSchema::web(18),
);

let projection = Crs::EPSG3857
.get_projection()
.expect("must find projection");

let feature_layer = FeatureLayer::new(
[
latlon!(53.732562, -1.863383),
latlon!(53.728265, -1.839966),
latlon!(53.704014, -1.786128),
]
.iter()
.map(|point| PointMarker {
point: projection.project(point).unwrap(),
..Default::default()
})
.collect(),
ColoredPointSymbol {},
Crs::EPSG3857,
);

let feature_layer = Arc::new(RwLock::new(feature_layer));

builder
.center(latlon!(53.732562, -1.863383))
.resolution(30.0)
.with_layer(feature_layer.clone())
.with_event_handler(move |ev, map| {
if let UserEvent::PointerMoved(event) = ev {
let mut layer = feature_layer.write().unwrap();

let Some(position) = map.view().screen_to_map(event.screen_pointer_position) else {
return EventPropagation::Stop;
};

for mut feature_container in layer.features_mut().iter_mut() {
feature_container.as_mut().highlighted = false;
}

for mut feature_container in
layer.get_features_at_mut(&position, map.view().resolution() * 20.0)
{
let state = feature_container.as_ref().highlighted;
feature_container.as_mut().highlighted = !state;
}

map.redraw();
}
galileo::control::EventPropagation::Propagate
})
.build()
.await
.run();
}

impl Symbol<PointMarker> for ColoredPointSymbol {
fn render<'a, N, P>(
&self,
feature: &PointMarker,
geometry: &'a Geom<P>,
_min_resolution: f64,
) -> Vec<RenderPrimitive<'a, N, P, Contour<P>, Polygon<P>>>
where
N: AsPrimitive<f32>,
P: CartesianPoint3d<Num = N> + Clone,
{
if let Geom::Point(point) = geometry {
vec![RenderPrimitive::new_point_ref(
point,
PointPaint::image(
if feature.highlighted {
GREEN_PIN_IMAGE.clone()
} else {
YELLOW_PIN_IMAGE.clone()
},
Vector2::new(0.5, 0.5),
1.0,
),
)]
} else {
vec![]
}
}
}
Loading

0 comments on commit b6fce29

Please sign in to comment.