Skip to content

Commit b0beab3

Browse files
committed
fix docu types, add table special filtering, show mutlivalues
1 parent 714e33f commit b0beab3

File tree

7 files changed

+296
-69
lines changed

7 files changed

+296
-69
lines changed

Readme.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ It provides a user-friendly interface to explore the relationships and propertie
2222
The application is suitable for developers, researchers, and anyone interested in working with semantic web technologies.
2323

2424
My primary goal was to ensure the program runs as fast as possible and maintains high performance even when handling large datasets.
25-
Therefore, the program has been optimized to efficiently process and manage large amount of triples and records.
25+
Therefore, the program has been optimized to efficiently process and manage large number of triples and records.
2626

2727
![screenshot](documentation/screeshots/rdf-glance_anim.gif)
2828

29-
Individualy styled visualisation of nodes and edges
29+
Individually styled visualization of nodes and edges
3030

3131
![screenshot](documentation/screeshots/milenium_falcon_pilot_movies.png)
3232

@@ -45,7 +45,7 @@ The RDF Data can be loaded by using following formats:
4545

4646
- ttl
4747
- rdf/xml
48-
- trig - namded graphs are ignored
48+
- trig - named graphs are ignored
4949
- nt (n-tuples)
5050
- nq (n-quads) - named graphs are ignored
5151

@@ -118,7 +118,7 @@ For more information on how to use RDFGlance, refer to the documentation provide
118118
## Known Problems
119119

120120
- Some RDF files can not be read. You will see the error messages in the std output. It seems that the used oxrdf parser is quite sensitive.
121-
- If loading big rdf files the gui may freeze for a while. There is no seperate worker thread for it
121+
- If loading big rdf files the gui may freeze for a while. There is no separate worker thread for it
122122
- The is no limitations to shown size of elements in visual graph. If you have more than 10000 Elements the ui may be not very responsible
123123

124124
I still hope that the application can be useful for others.
@@ -133,7 +133,7 @@ Unlike traditional web applications that rely on HTML and React, `egui` allows f
133133
RDFGlance uses some oxigraph rust libraries.
134134

135135
I have developed the application because my frustration about low performance of existing rdf solutions and I wanted to learn and test Rust and egui framework.
136-
It is learn and hobby project.
136+
It is a learning and hobby project.
137137

138138
## Releases
139139

documentation/manual.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# General Information
22

3-
At this time the application may not be self-explained. So here some screenshot with additional informations.
3+
At this time the application may not be self-explained. So here some screenshot with additional information.
44

55
First you should load some rdf data. The application supports ttl (turtle) and rdf/xml formats.
66
There is some sample rdf data in the [sample-rdf-data](../sample-rdf-data/programming_languages.ttl) directory
77

8-
After it you may chosse 3 possilbe note tabs: Table, Visual Graph and Browser
8+
After it you may chose 3 possible note tabs: Table, Visual Graph and Browser
99
The good point is to start is table view.
1010

1111
# Table Tab
@@ -15,17 +15,17 @@ The good point is to start is table view.
1515
RDFGlance sort all nodes by types.
1616
First you can see some statistics of RDF Date.
1717

18-
Then you can see the list of all types with some statistice
18+
Then you can see the list of all types with some statistic
1919
- count of all instances of this type
2020
- count of unique data properties of all instances of this type
21-
- count of unique object properties of all isntances of this type
22-
- count of unique referenced object properties of all isntance of this type
21+
- count of unique object properties of all instances of this type
22+
- count of unique referenced object properties of all instances of this type
2323

2424
Remember that in rdfs an instance can have multiple types.
2525

2626
After selecting the type you can see all instances as a table.
2727
You can sort the instances by some data property.
28-
The out/in columns shown the cound of outgoing and ingoing edges (object properties).
28+
The out/in columns shown the count of outgoing and ingoing edges (object properties).
2929
By clicking of the out/in cell you can browser all references in pop-up windows.
3030

3131
You can click on the cell to see the whole value of the data property or other values of the same predicate.
@@ -37,12 +37,12 @@ Remember in rdf one node can have multiple objects of same predicate.
3737

3838
In the browser you can see all properties of one node.
3939

40-
Following informations are show:
40+
Following information are show:
4141

4242
- type of the node (as label). You can click the type to change to table view and see all instances of clicked type
4343
- all data properties
4444
- all object properties (references)
45-
- all objects that references this node (referenced by)
45+
- all objects that reference this node (referenced by)
4646

4747
# Visual Graph
4848

src/drawing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ pub fn draw_node_label(
268268
};
269269
let node_rect = if show_labels {
270270
let stroke = if type_style.border_width > 0.0 {
271-
Stroke::new(type_style.border_width, fade_color(type_style.color,faded))
271+
Stroke::new(type_style.border_width, fade_color(type_style.border_color,faded))
272272
} else {
273273
Stroke::NONE
274274
};

src/graph_view.rs

Lines changed: 117 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,52 @@ struct ReferencesState {
1616
pub visible: u32,
1717
}
1818

19+
enum NodeContextAction {
20+
None,
21+
Hide,
22+
HideThisType,
23+
HideOther,
24+
HideOtherTypes,
25+
HideUnrelated,
26+
Expand,
27+
ExpandReferenced,
28+
ExpandReferencedBy,
29+
ExpandThisType,
30+
}
31+
32+
impl NodeContextAction {
33+
fn show_menu(ui: &mut egui::Ui) -> NodeContextAction {
34+
if ui.button("Hide").clicked() {
35+
return NodeContextAction::Hide;
36+
}
37+
if ui.button("Hide this type").clicked() {
38+
return NodeContextAction::HideThisType;
39+
}
40+
if ui.button("Hide other").clicked() {
41+
return NodeContextAction::HideOther;
42+
}
43+
if ui.button("Hide other types").clicked() {
44+
return NodeContextAction::HideOtherTypes;
45+
}
46+
if ui.button("Hide unrelated").clicked() {
47+
return NodeContextAction::HideUnrelated;
48+
}
49+
if ui.button("Expand").clicked() {
50+
return NodeContextAction::Expand;
51+
}
52+
if ui.button("Expand Referenced").clicked() {
53+
return NodeContextAction::ExpandReferenced;
54+
}
55+
if ui.button("Expand Referenced By").clicked() {
56+
return NodeContextAction::ExpandReferencedBy;
57+
}
58+
if ui.button("Expand this type").clicked() {
59+
return NodeContextAction::ExpandThisType;
60+
}
61+
return NodeContextAction::None;
62+
}
63+
}
64+
1965
impl RdfGlanceApp {
2066
pub fn show_graph(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) -> NodeAction {
2167
let mut node_to_click: NodeAction = NodeAction::None;
@@ -562,54 +608,77 @@ impl RdfGlanceApp {
562608
let mut close_menu = false;
563609
let current_index = *node_index;
564610
// TODO need to clone the types to release the mutable borrow from current_node (node_data)
565-
let types = current_node.types.clone();
566-
if ui.button("Hide").clicked() {
567-
self.visible_nodes.remove(current_index);
568-
self.ui_state.compute_layout = true;
569-
close_menu = true;
570-
}
571-
if ui.button("Hide this Type").clicked() {
572-
self.ui_state.compute_layout = true;
573-
self.visible_nodes.remove(current_index);
574-
self.visible_nodes.nodes.retain(|x| {
575-
let node = self.node_data.get_node_by_index(x.node_index);
576-
if let Some((_,node)) = node {
577-
!node.has_same_type(&types)
578-
} else {
579-
true
580-
}
581-
});
582-
close_menu = true;
583-
}
584-
if ui.button("Hide other").clicked() {
585-
self.ui_state.compute_layout = true;
586-
self.visible_nodes.nodes.clear();
587-
self.visible_nodes.add_by_index(current_index);
588-
close_menu = true;
589-
}
590-
if ui.button("Hide other Types").clicked() {
591-
self.ui_state.compute_layout = true;
592-
self.visible_nodes.nodes.retain(|x| {
593-
let node = self.node_data.get_node_by_index(x.node_index);
594-
if let Some((_,node)) = node {
595-
node.has_same_type(&types)
596-
} else {
597-
false
598-
}
599-
});
600-
close_menu = true;
601-
}
602-
if ui.button("Expand").clicked() {
603-
self.expand_node(current_index, ExpandType::Both);
604-
close_menu = true;
605-
}
606-
if ui.button("Expand Referenced").clicked() {
607-
self.expand_node(current_index, ExpandType::References);
608-
close_menu = true;
609-
}
610-
if ui.button("Expand Referenced By").clicked() {
611-
self.expand_node(current_index, ExpandType::ReverseReferences);
612-
close_menu = true;
611+
let node_action = NodeContextAction::show_menu(ui);
612+
match node_action {
613+
NodeContextAction::Hide => {
614+
self.visible_nodes.remove(current_index);
615+
self.ui_state.compute_layout = true;
616+
close_menu = true;
617+
},
618+
NodeContextAction::HideThisType => {
619+
let types = current_node.types.clone();
620+
self.ui_state.compute_layout = true;
621+
self.visible_nodes.remove(current_index);
622+
self.visible_nodes.nodes.retain(|x| {
623+
let node = self.node_data.get_node_by_index(x.node_index);
624+
if let Some((_,node)) = node {
625+
!node.has_same_type(&types)
626+
} else {
627+
true
628+
}
629+
});
630+
close_menu = true;
631+
},
632+
NodeContextAction::HideOther => {
633+
self.ui_state.compute_layout = true;
634+
self.visible_nodes.nodes.clear();
635+
self.visible_nodes.add_by_index(current_index);
636+
close_menu = true;
637+
},
638+
NodeContextAction::HideOtherTypes => {
639+
let types = current_node.types.clone();
640+
self.ui_state.compute_layout = true;
641+
self.visible_nodes.nodes.retain(|x| {
642+
let node = self.node_data.get_node_by_index(x.node_index);
643+
if let Some((_,node)) = node {
644+
node.has_same_type(&types)
645+
} else {
646+
false
647+
}
648+
});
649+
close_menu = true;
650+
},
651+
NodeContextAction::HideUnrelated => {
652+
self.visible_nodes.nodes.retain(|x| {
653+
if x.node_index == *node_index {
654+
return true;
655+
}
656+
current_node.references.iter().any(| (_predicate_index, ref_iri)| *ref_iri == x.node_index) ||
657+
current_node.reverse_references.iter().any(| (_predicate_index, ref_iri)| *ref_iri == x.node_index)
658+
});
659+
self.ui_state.compute_layout = true;
660+
close_menu = true;
661+
},
662+
NodeContextAction::Expand => {
663+
self.expand_node(current_index, ExpandType::Both);
664+
close_menu = true;
665+
},
666+
NodeContextAction::ExpandReferenced => {
667+
self.expand_node(current_index, ExpandType::References);
668+
close_menu = true;
669+
},
670+
NodeContextAction::ExpandReferencedBy => {
671+
self.expand_node(current_index, ExpandType::ReverseReferences);
672+
close_menu = true;
673+
},
674+
NodeContextAction::ExpandThisType => {
675+
let types = current_node.types.clone();
676+
self.expand_all_by_types(&types);
677+
close_menu = true;
678+
},
679+
NodeContextAction::None => {
680+
// do nothing
681+
}
613682
}
614683
if close_menu {
615684
self.ui_state.context_menu_node = None;

src/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,39 @@ impl RdfGlanceApp {
500500
}
501501
npos.position(&mut self.visible_nodes);
502502
}
503+
fn expand_all_by_types(&mut self, types: &Vec<IriIndex>) {
504+
let mut refs_to_expand: HashSet<IriIndex> = HashSet::new();
505+
let mut parent_ref : Vec<(IriIndex,IriIndex)> = Vec::new();
506+
for visible_index in &self.visible_nodes.nodes {
507+
if let Some((_,nnode)) = self.node_data.get_node_by_index(visible_index.node_index) {
508+
for (_, ref_iri) in nnode.references.iter() {
509+
if let Some((_,nnode)) = self.node_data.get_node_by_index(*ref_iri) {
510+
if nnode.match_types(types) {
511+
if refs_to_expand.insert(*ref_iri) {
512+
parent_ref.push((visible_index.node_index,*ref_iri));
513+
}
514+
}
515+
}
516+
}
517+
for (_, ref_iri) in nnode.reverse_references.iter() {
518+
if let Some((_,nnode)) = self.node_data.get_node_by_index(*ref_iri) {
519+
if nnode.match_types(types) {
520+
if refs_to_expand.insert(*ref_iri) {
521+
parent_ref.push((visible_index.node_index,*ref_iri));
522+
}
523+
}
524+
}
525+
}
526+
}
527+
}
528+
let mut npos = NeighbourPos::new();
529+
for (parent_index,ref_index) in parent_ref {
530+
if !self.visible_nodes.contains(ref_index) && self.load_object_by_index(ref_index) {
531+
npos.insert(parent_index, ref_index);
532+
}
533+
}
534+
npos.position(&mut self.visible_nodes);
535+
}
503536
pub fn load_ttl(&mut self, file_name: &str) {
504537
let language_filter = self.persistent_data.config_data.language_filter();
505538
let rdfttl = rdfwrap::RDFWrap::load_file(file_name, &mut self.node_data, &language_filter, &mut self.prefix_manager);

src/nobject.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use indexmap::IndexMap;
22
use oxrdf::vocab::rdf;
3+
use rand::rand_core::le;
34

45
use crate::{config::IriDisplay, prefix_manager::PrefixManager, string_indexer::{IndexSpan, StringCache, StringIndexer}, GVisualisationStyle};
56

@@ -168,6 +169,44 @@ impl NObject {
168169
None
169170
}
170171

172+
pub fn get_property_count(&self, predicate_index: IriIndex, language_index: LangIndex) -> Option<(&ObjectType,u32)> {
173+
let mut no_lang: Option<&ObjectType> = None;
174+
let mut fallback_lang: Option<&ObjectType> = None;
175+
let mut lang_value: Option<&ObjectType> = None;
176+
let mut count: u32 = 0;
177+
for (predicate, value) in &self.properties {
178+
if predicate == &predicate_index {
179+
count += 1;
180+
match value {
181+
ObjectType::LangString(lang, _) => {
182+
if *lang==language_index && lang_value.is_none() {
183+
lang_value = Some(value);
184+
}
185+
if *lang == 0 && fallback_lang.is_none() {
186+
fallback_lang = Some(value);
187+
}
188+
}
189+
ObjectType::String(_) | ObjectType::TypedString(_, _) | ObjectType::StringShort(_)=> {
190+
if no_lang.is_none() {
191+
no_lang = Some(value);
192+
}
193+
}
194+
}
195+
}
196+
}
197+
if let Some(lang) = lang_value {
198+
return Some((lang,count));
199+
}
200+
if let Some(fallback_lang) = fallback_lang {
201+
return Some((fallback_lang,count));
202+
}
203+
if let Some(no_lang) = no_lang {
204+
return Some((no_lang, count));
205+
}
206+
None
207+
}
208+
209+
171210
pub fn apply_filter(&self, filter: &str, iri: &str, indexers: &Indexers) -> bool {
172211
if iri.contains(filter) {
173212
return true;
@@ -179,6 +218,24 @@ impl NObject {
179218
}
180219
false
181220
}
221+
222+
pub fn match_types(&self, types: &Vec<IriIndex>) -> bool {
223+
for type_index in self.types.iter() {
224+
if types.contains(type_index) {
225+
return true;
226+
}
227+
}
228+
false
229+
}
230+
231+
pub fn has_property(&self, predicate_index: IriIndex) -> bool {
232+
for (predicate, _value) in &self.properties {
233+
if predicate == &predicate_index {
234+
return true;
235+
}
236+
}
237+
false
238+
}
182239
}
183240

184241
impl Default for Indexers {

0 commit comments

Comments
 (0)