Skip to content

Commit

Permalink
Add labels example for FeatureLayer
Browse files Browse the repository at this point in the history
  • Loading branch information
Maximkaaa committed Feb 12, 2025
1 parent 72b063c commit 6a1014e
Show file tree
Hide file tree
Showing 4 changed files with 339 additions and 0 deletions.
304 changes: 304 additions & 0 deletions galileo/examples/labels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
//! Labels in feature layers
use std::sync::Arc;

use bytes::Bytes;
use eframe::CreationContext;
use galileo::layer::feature_layer::Feature;
use galileo::layer::raster_tile_layer::RasterTileLayerBuilder;
use galileo::layer::FeatureLayer;
use galileo::render::point_paint::PointPaint;
use galileo::render::render_bundle::RenderPrimitive;
use galileo::render::text::font_service::FontService;
use galileo::render::text::{HorizontalAlignment, TextStyle, VerticalAlignment};
use galileo::symbol::Symbol;
use galileo::{Color, Map, MapBuilder};
use galileo_egui::{EguiMap, EguiMapState};
use galileo_types::geo::impls::GeoPoint2d;
use galileo_types::geo::Crs;
use galileo_types::geometry::Geom;
use galileo_types::geometry_type::GeoSpace2d;
use galileo_types::latlon;
use parking_lot::RwLock;

struct EguiMapApp {
map: EguiMapState,
feature_layer: Arc<RwLock<FeatureLayer<GeoPoint2d, LabeledPoint, LabeledSymbol, GeoSpace2d>>>,
font_size: f32,
horizontal_align: HorizontalAlignment,
vertical_align: VerticalAlignment,
is_bold: bool,
is_italic: bool,
}

impl EguiMapApp {
fn new(mut map: Map, cc: &CreationContext) -> Self {
let layer = FeatureLayer::new(points(), LabeledSymbol::new(), Crs::EPSG3857);
let layer = Arc::new(RwLock::new(layer));

map.layers_mut().push(layer.clone());

Self {
map: EguiMapState::new(
map,
cc.egui_ctx.clone(),
cc.wgpu_render_state.clone().expect("no render state"),
[],
),
feature_layer: layer,
font_size: 20.0,
horizontal_align: HorizontalAlignment::Center,
vertical_align: VerticalAlignment::Middle,
is_bold: false,
is_italic: false,
}
}

fn update_symbol(&mut self) {
let symbol = LabeledSymbol {
style: TextStyle {
font_name: "Noto Sans".to_string(),
font_size: self.font_size,
font_color: Color::BLACK,
horizontal_alignment: self.horizontal_align,
vertical_alignment: self.vertical_align,
},
};

self.feature_layer.write().set_symbol(symbol);
}

fn horizontal_alignment(&mut self, ui: &mut egui::Ui) {
ui.horizontal(|ui| {
if ui
.selectable_label(self.horizontal_align == HorizontalAlignment::Left, "Left")
.clicked()
&& self.horizontal_align != HorizontalAlignment::Left
{
self.horizontal_align = HorizontalAlignment::Left;
self.update_symbol();
}

if ui
.selectable_label(
self.horizontal_align == HorizontalAlignment::Center,
"Center",
)
.clicked()
&& self.horizontal_align != HorizontalAlignment::Center
{
self.horizontal_align = HorizontalAlignment::Center;
self.update_symbol();
}

if ui
.selectable_label(self.horizontal_align == HorizontalAlignment::Right, "Right")
.clicked()
&& self.horizontal_align != HorizontalAlignment::Right
{
self.horizontal_align = HorizontalAlignment::Right;
self.update_symbol();
}
});
}

fn vertical_alignment(&mut self, ui: &mut egui::Ui) {
ui.horizontal(|ui| {
if ui
.selectable_label(self.vertical_align == VerticalAlignment::Top, "Top")
.clicked()
&& self.vertical_align != VerticalAlignment::Top
{
self.vertical_align = VerticalAlignment::Top;
self.update_symbol();
}

if ui
.selectable_label(self.vertical_align == VerticalAlignment::Middle, "Middle")
.clicked()
&& self.vertical_align != VerticalAlignment::Middle
{
self.vertical_align = VerticalAlignment::Middle;
self.update_symbol();
}

if ui
.selectable_label(self.vertical_align == VerticalAlignment::Bottom, "Bottom")
.clicked()
&& self.vertical_align != VerticalAlignment::Bottom
{
self.vertical_align = VerticalAlignment::Bottom;
self.update_symbol();
}
});
}
}

impl eframe::App for EguiMapApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
EguiMap::new(&mut self.map).show_ui(ui);

egui::Window::new("Galileo map").show(ctx, |ui| {
ui.label("Text format:");

ui.horizontal(|ui| {
ui.label("Font size: ");
if ui
.add(
egui::DragValue::new(&mut self.font_size)
.speed(0.1)
.range(1.0..=50.0),
)
.changed()
{
self.update_symbol();
}
});

self.horizontal_alignment(ui);
self.vertical_alignment(ui);

ui.horizontal(|ui| {
if ui.selectable_label(self.is_bold, "Bold").clicked() {
self.is_bold = !self.is_bold;
self.update_symbol();
}

if ui.selectable_label(self.is_italic, "Italic").clicked() {
self.is_italic = !self.is_italic;
self.update_symbol();
}
});
});
});
}
}

#[cfg(not(target_arch = "wasm32"))]
fn main() {
run()
}

pub(crate) fn run() {
let map = create_map();
galileo_egui::init_with_app(Box::new(|cc| Ok(Box::new(EguiMapApp::new(map, cc)))))
.expect("failed to initialize");
}

fn create_map() -> Map {
FontService::with_mut(|service| {
let font = include_bytes!("data/NotoSansAdlam-Regular.ttf");
service
.load_fonts(Bytes::from_static(font))
.expect("failed to load font");
});

let layer = RasterTileLayerBuilder::new_osm()
.with_file_cache_checked(".tile_cache")
.build()
.expect("failed to create layer");

MapBuilder::default().with_layer(layer).build()
}

fn points() -> Vec<LabeledPoint> {
vec![
LabeledPoint {
position: latlon!(0.0, 0.0),
label: "Behold Galileo - cross-platform map rendering engine",
},
LabeledPoint {
position: latlon!(5.0, 0.0),
label: "Вот Galileo – кроссплатформенный движок рендеринга карт",
},
LabeledPoint {
position: latlon!(10.0, 0.0),
label: "보라, Galileo - 크로스 플랫폼 지도 렌더링 엔진",
},
LabeledPoint {
position: latlon!(15.0, 0.0),
label: "ها هو Galileo - محرك عرض الخرائط عبر الأنظمة الأساسية",
},
LabeledPoint {
position: latlon!(20.0, 0.0),
label: "הנה Galileo - מנוע רינדור מפות חוצה פלטפורמות",
},
LabeledPoint {
position: latlon!(25.0, 0.0),
label: "देखो Galileo - क्रॉस-प्लेटफ़ॉर्म मैप रेंडरिंग इंजन",
},
LabeledPoint {
position: latlon!(30.0, 0.0),
label: "看哪,Galileo——跨平台地图渲染引擎",
},
LabeledPoint {
position: latlon!(35.0, 0.0),
label: "हेर, Galileo - क्रस-प्लेटफर्म नक्सा रेन्डरिङ इन्जिन।",
},
]
}

struct LabeledPoint {
position: GeoPoint2d,
label: &'static str,
}

impl Feature for LabeledPoint {
type Geom = GeoPoint2d;

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

struct LabeledSymbol {
style: TextStyle,
}

impl LabeledSymbol {
fn new() -> Self {
Self {
style: TextStyle {
font_name: "Noto Sans".to_string(),
font_size: 20.0,
font_color: Color::BLACK,
horizontal_alignment: Default::default(),
vertical_alignment: Default::default(),
},
}
}
}

impl Symbol<LabeledPoint> for LabeledSymbol {
fn render<'a, N, P>(
&self,
feature: &LabeledPoint,
geometry: &'a galileo_types::geometry::Geom<P>,
_min_resolution: f64,
) -> Vec<
galileo::render::render_bundle::RenderPrimitive<
'a,
N,
P,
galileo_types::impls::Contour<P>,
galileo_types::impls::Polygon<P>,
>,
>
where
N: num_traits::AsPrimitive<f32>,
P: galileo_types::cartesian::CartesianPoint3d<Num = N> + Clone,
{
let Geom::Point(point) = geometry else {
return vec![];
};

vec![
RenderPrimitive::new_point(point.clone(), PointPaint::circle(Color::BLUE, 3.0)),
RenderPrimitive::new_point(
point.clone(),
PointPaint::label_owned(feature.label.to_string(), self.style.clone()),
),
]
}
}
4 changes: 4 additions & 0 deletions galileo/src/layer/feature_layer/feature_render_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ impl FeatureRenderStore {
}
}

pub fn reset(&self) -> Self {
Self::new(self.id, self.min_resolution, self.buffer_size_limit)
}

fn has_not_full_bundles(&self) -> bool {
self.render_bundles
.iter()
Expand Down
10 changes: 10 additions & 0 deletions galileo/src/layer/feature_layer/feature_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,16 @@ impl<F> FeatureStore<F> {
std::mem::take(&mut *updates)
}

pub(super) fn reset(&self) {
let mut updates = self.pending_updates.lock();
for (index, entry) in self.features.iter().enumerate() {
entry.render_indices.lock().clear();
updates.push(FeatureUpdate::Update {
feature_index: index,
});
}
}

/// Iterates over immutable containers of the features.
pub fn iter(&self) -> impl Iterator<Item = FeatureContainer<F>> {
self.features
Expand Down
21 changes: 21 additions & 0 deletions galileo/src/layer/feature_layer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,27 @@ where
pub fn crs(&self) -> &Crs {
&self.crs
}

/// Changes the symbol of the layer.
pub fn set_symbol(&mut self, symbol: S) {
self.symbol = symbol;
self.drop_render_cache();
self.request_redraw();
}

fn drop_render_cache(&mut self) {
for lod in &mut self.lods {
let mut content = lod.contents.lock();
*content = content.reset();
}
self.features.reset();
}

fn request_redraw(&self) {
if let Some(messenger) = self.messenger.read().as_ref() {
messenger.request_redraw();
}
}
}

impl<P, F, S> FeatureLayer<P, F, S, GeoSpace2d>
Expand Down

0 comments on commit 6a1014e

Please sign in to comment.