Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 53 additions & 23 deletions src/openalea/rsml/io.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""" XML SmartRoot / RootNav reader and writer
""" XML SmartRoot / RootNav / RootSystemTracker reader and writer

TODO:
* Manage metadata
Expand All @@ -18,7 +18,7 @@

"""
##############################################################################
# XML SmartRoot / RootNav reader and writer
# XML SmartRoot / RootNav / RootSystemTracker reader and writer
##############################################################################

from ast import literal_eval
Expand Down Expand Up @@ -48,7 +48,18 @@ def parse(self, filename, debug=False):
root = doc.getroot()
# recursive call of the functions to add neww plants/root axis to the MTG
self.dispatch(root)


# if some functions are defined in the MTG properties but not in metadata, add them
graph = self._g
if graph.graph_properties().get('metadata', {}).get('functions') is None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get just ones graph_properties rather than calling each time.
Code will be simpler, nicer.

props = graph.graph_properties()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or directly for metadata

metadata = graph.graph_properties().set_default('metadata', {})

graph.graph_properties()['metadata']['functions'] = []
if graph.properties().get('time'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

props is a dict, isn't it?

Just do

if 'time' in props:

graph.graph_properties()['metadata']['functions'].append('time')
if graph.properties().get('time_hours'):
graph.graph_properties()['metadata']['functions'].append('time_hours')
if graph.properties().get('diameter'):
graph.graph_properties()['metadata']['functions'].append('diameter')

g = fat_mtg(self._g)

# Add metadata as property of the graph
Expand Down Expand Up @@ -87,6 +98,7 @@ def metadata(self, elts, **properties):
meta = self._metadata = dict()
gprop = self._g.graph_properties()
#print([elt.tag for elt in elts])
pixel_size = None
for elt in elts:
elt_tag = elt.tag
#print(elt_tag)
Expand All @@ -97,12 +109,20 @@ def metadata(self, elts, **properties):
elif elt_tag in ['user','file-key','software','unit']:
meta[elt_tag] = elt.text
elif elt_tag in ["property-definitions","time-sequence","image",'private']:
#print(elt_tag)
self.dispatch(elt)
elif elt_tag == "observation-hours":
elt_text = elt.text
meta[elt_tag] = [literal_eval(v) for v in elt_text.split(',') if v]
elif elt_tag in ['size', 'pixel_size']:
pixel_size = float(elt.text) # RootSystemTracker use size for pixel_size before image element D:
elif elt_tag=='mtg_graph_properties':
gprop.update(read_xml_tree(elt))
else:
meta[elt_tag] = read_xml_tree(elt)

if pixel_size:
meta['resolution'] = meta.get('image',{})
meta['image']['resolution'] = pixel_size

gprop['metadata'] = meta

Expand Down Expand Up @@ -134,7 +154,7 @@ def function_definition(self, elts, **properties):
label = prop.pop('label')
if label:
self._propdef[label]=prop

def time_sequence(self, elts, **properties):
""" A plant with parameters and a recursive structure.

Expand Down Expand Up @@ -218,6 +238,7 @@ def polyline(self, elts, **properties):
self._polyline = [] # will store all points in `elts`
self._time = []
self._time_hours = []
# self._diameter = []
for elt in elts:
self.dispatch(elt)

Expand All @@ -230,13 +251,16 @@ def polyline(self, elts, **properties):
if self._time_hours :
self._node.time_hours = self._time_hours
self._time_hours = None

# if self._diameter :
# self._node.diameter = self._diameter
# self._diameter = None

def point(self, elts, **properties):
poly = self._polyline
point = []
times = self._time
times_hours = self._time_hours
# diameters = self._diameter
if properties:
if 'x' in properties or 'coord_x' in properties:
coords = ['x', 'y', 'z']
Expand All @@ -250,13 +274,14 @@ def point(self, elts, **properties):
coords = ['th', 'coord_th']
time_hours = [float(properties[c]) for c in coords if c in properties]
times_hours.append(time_hours[0])
# if 'diameter' in properties:
# diameter = float(properties['diameter'])
# diameters.append(diameter)
else:
point = [float(elt.text) for elt in elts]
poly.append(point)

#print('point', point)


#print('point', point)

def functions(self, elts, **properties):
""" A root axis with geometry, functions, properties.
Expand Down Expand Up @@ -412,18 +437,26 @@ def mtg(self):
def metadata(self):
g = self._g
self.xml_meta = xml.SubElement(self.xml_root,'metadata')

gmetadata = metadata.set_metadata(g)

for tag in metadata.flat_metadata:
self.SubElement(self.xml_meta, tag=tag, text=str(gmetadata[tag]))

# image metadata
self.observation_hours(gmetadata)
self.image(gmetadata)
self.property_definitions(gmetadata)
# print('TODO: time-sequence')

def observation_hours(self,metadata):
""" dump observation-hours element of metadata """
obs = metadata.get('observation-hours') # List of observation hours
if obs is None: return

obs_elt = self.SubElement(self.xml_meta, 'observation-hours')
txt = ','.join(str(hour) for hour in obs)
obs_elt.text = txt

def image(self,metadata):
""" dump image element of metadata """
image = metadata.get('image')
Expand Down Expand Up @@ -485,8 +518,6 @@ def scene(self):

# self.process_vertex(vid)



def plant(self, vid):
g = self._g

Expand All @@ -512,16 +543,17 @@ def root(self, xml_parent, mtg_vid):
self.xml_nodes[vid] = axis = self.SubElement(xml_parent, 'root')

# set xml attributes
props = g[vid]
props = g[vid]
axis.attrib['id'] = str(props.pop('id', vid))
axis.attrib['label'] = str(props.pop('label', g.label(vid)))
if 'po:accession' in props:
axis.attrib['po:accession'] = str(props.pop('po:accession'))

# set xml axis element
self.properties(vid, axis)
##self.functions(axis,**props)
self.geometry(axis,**props)
self.functions(axis,**props)
self.properties(vid, axis)


# process children root axis
# --------------------------
Expand Down Expand Up @@ -583,21 +615,19 @@ def functions(self, axis, **props):
for tag in pname:
if tag in props:
if functions_elt is None:
functions_elt = self.SubElement(xml_elt, 'functions')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good.

functions_elt = self.SubElement(axis, 'functions')
function_elt = self.SubElement(functions_elt, 'function')
function_elt.attrib['domain'] = 'polyline'
function_elt.attrib['name'] = tag
function_elt.attrib['name'] = tag

for sample in attrib[tag]:
for sample in props[tag]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep.

sample_elt = self.SubElement(function_elt, 'sample')
if isinstance(sample, (tuple, list)) and len(sample) == 2:
sample_elt.attrib['position'] = str(sample[0])
sample_elt.attrib['value'] = str(sample[1])
else:
sample_elt.attrib['value'] = str(sample)



##########################################################################
# Wrapper functions for OpenAlea usage.

Expand All @@ -621,4 +651,4 @@ def mtg2rsml(g, rsml_file):
with open(rsml_file, 'wb') as f: # F. Bauget 2022-04-11: with python 3 xml.tostring(self.xml_root, encoding='UTF-8') gives bytes so I open in binary mode
f.write(s)
else:
rsml_file.write(s)
rsml_file.write(s)
2 changes: 1 addition & 1 deletion src/openalea/rsml/matching.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def match_plants(t1,t2, max_distance=None):

The matching is done usinf `one_to_one_match`
"""
from operator import div
from operator import truediv as div

# compute seed position of plants in t
# ------------------------------------
Expand Down
13 changes: 10 additions & 3 deletions src/openalea/rsml/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,13 @@
function also fill missing items, folowing the specified behavior describe in
the function documentation.
"""
import xml.etree.ElementTree as xml


# ordered list of metadata attribute name
flat_metadata = ['version','unit','resolution','software','user',
'last-modified','file-key']
metadata_names = flat_metadata + ['image', 'property-definitions',
'function-definitions', 'time-sequence',
'observation-hours',
'private']

# default values
Expand Down Expand Up @@ -91,9 +90,17 @@ def set_metadata(g):
from os.path import getctime
creation = getctime(image['name'])
image['captured'] = datetime.fromtimestamp(creation).isoformat()
except KeyError: # not defined
pass
except OSError: # no such file
pass


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not...
Security flaw but ... we do not care

if 'observation-hours' in metadata:
# table of observation times
obs = metadata['observation-hours']
if isinstance(obs, str):
import ast
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import at the begining of the file with ... imports

metadata['observation-hours'] = list(ast.literal_eval(obs)) # convert string to list

if metadata['file-key']!=default['file-key']:
import uuid
Expand Down
Loading