Skip to content

Commit

Permalink
Add Graph.load_g2o() classmethod and deprecate the load_g2o() standal…
Browse files Browse the repository at this point in the history
…one function (#72)
  • Loading branch information
JeffLIrion committed Nov 11, 2023
1 parent 5725b4b commit 391b4f3
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 81 deletions.
8 changes: 4 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ SE(3) Dataset

.. code-block:: python
>>> from graphslam.load import load_g2o
>>> from graphslam.graph import Graph
>>> g = load_g2o("data/parking-garage.g2o") # https://lucacarlone.mit.edu/datasets/
>>> g = Graph.load_g2o("data/parking-garage.g2o") # https://lucacarlone.mit.edu/datasets/
>>> g.plot(vertex_markersize=1)
Expand Down Expand Up @@ -79,9 +79,9 @@ SE(2) Dataset

.. code-block:: python
>>> from graphslam.load import load_g2o
>>> from graphslam.graph import Graph
>>> g = load_g2o("data/input_INTEL.g2o") # https://lucacarlone.mit.edu/datasets/
>>> g = Graph.load_g2o("data/input_INTEL.g2o") # https://lucacarlone.mit.edu/datasets/
>>> g.plot()
Expand Down
5 changes: 3 additions & 2 deletions graphslam/edge/base_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ def to_g2o(self):
Returns
-------
str
The edge in .g2o format
str, None
The edge in .g2o format, or ``None`` if writing to g2o format is not supported
"""

Expand All @@ -182,6 +182,7 @@ def from_g2o(cls, line):
-------
BaseEdge, None
The instantiated edge object, or ``None`` if ``line`` does not correspond to this edge type
(or if this edge type does not support loading from g2o)
"""

Expand Down
79 changes: 79 additions & 0 deletions graphslam/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@

from collections import defaultdict
from functools import reduce
import logging
import time
import warnings

Expand All @@ -90,6 +91,12 @@
except ImportError: # pragma: no cover
plt = None

from .edge.base_edge import BaseEdge
from .edge.edge_odometry import EdgeOdometry
from .vertex import Vertex


_LOGGER = logging.getLogger(__name__)

warnings.simplefilter("ignore", SparseEfficiencyWarning)
warnings.filterwarnings("ignore", category=SparseEfficiencyWarning)
Expand Down Expand Up @@ -512,6 +519,78 @@ def to_g2o(self, outfile):
if edge_str_or_none:
f.write(edge_str_or_none)

@classmethod
def load_g2o(cls, infile, custom_edge_types=None):
r"""Load a graph from a .g2o file.
Parameters
----------
infile : str
The path to the .g2o file
custom_edge_types : list[type], None
A list of custom edge types, which must be subclasses of ``BaseEdge``
Returns
-------
Graph
The loaded graph
"""
edges = []
vertices = []

custom_edge_types = custom_edge_types or []
for edge_type in custom_edge_types:
assert issubclass(edge_type, BaseEdge)

def custom_edge_from_g2o(line, custom_edge_types):
"""Load a custom edge from a .g2o line.
Parameters
----------
line : str
A line from a .g2o file
custom_edge_types : list[type]
A list of custom edge types, which must be subclasses of ``BaseEdge``
Returns
-------
BaseEdge, None
The instantiated edge object, or ``None`` if the line does not correspond to any of the custom edge types
"""
for custom_edge_type in custom_edge_types:
edge_or_none = custom_edge_type.from_g2o(line)
if edge_or_none:
return edge_or_none

return None

with open(infile) as f:
for line in f.readlines():
if line.strip():
# Vertex
vertex_or_none = Vertex.from_g2o(line)
if vertex_or_none:
vertices.append(vertex_or_none)
continue

# Custom edge types
custom_edge_or_none = custom_edge_from_g2o(line, custom_edge_types)
if custom_edge_or_none:
edges.append(custom_edge_or_none)
continue

# Odometry Edge
edge_or_none = EdgeOdometry.from_g2o(line)
if edge_or_none:
edges.append(edge_or_none)
continue

_LOGGER.warning("Line not supported -- '%s'", line.rstrip())

return cls(edges, vertices)

def plot(self, vertex_color="r", vertex_marker="o", vertex_markersize=3, edge_color="b", title=None):
"""Plot the graph.
Expand Down
75 changes: 10 additions & 65 deletions graphslam/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

import logging

from .edge.base_edge import BaseEdge
from .edge.edge_odometry import EdgeOdometry
from .graph import Graph
from .vertex import Vertex


_LOGGER = logging.getLogger(__name__)
Expand All @@ -32,60 +29,8 @@ def load_g2o(infile, custom_edge_types=None):
The loaded graph
"""
edges = []
vertices = []

custom_edge_types = custom_edge_types or []
for edge_type in custom_edge_types:
assert issubclass(edge_type, BaseEdge)

def custom_edge_from_g2o(line, custom_edge_types):
"""Load a custom edge from a .g2o line.
Parameters
----------
line : str
A line from a .g2o file
custom_edge_types : list[type]
A list of custom edge types, which must be subclasses of ``BaseEdge``
Returns
-------
BaseEdge, None
The instantiated edge object, or ``None`` if the line does not correspond to any of the custom edge types
"""
for custom_edge_type in custom_edge_types:
edge_or_none = custom_edge_type.from_g2o(line)
if edge_or_none:
return edge_or_none

return None

with open(infile) as f:
for line in f.readlines():
if line.strip():
# Vertex
vertex_or_none = Vertex.from_g2o(line)
if vertex_or_none:
vertices.append(vertex_or_none)
continue

# Custom edge types
custom_edge_or_none = custom_edge_from_g2o(line, custom_edge_types)
if custom_edge_or_none:
edges.append(custom_edge_or_none)
continue

# Odometry Edge
edge_or_none = EdgeOdometry.from_g2o(line)
if edge_or_none:
edges.append(edge_or_none)
continue

_LOGGER.warning("Line not supported -- '%s'", line.rstrip())

return Graph(edges, vertices)
_LOGGER.warning("load_g2o is deprecated; use Graph.load_g2o instead")
return Graph.load_g2o(infile, custom_edge_types)


def load_g2o_r2(infile):
Expand All @@ -102,8 +47,8 @@ def load_g2o_r2(infile):
The loaded graph
"""
_LOGGER.warning("load_g2o_r2 is deprecated; use load_g2o instead")
return load_g2o(infile)
_LOGGER.warning("load_g2o_r2 is deprecated; use Graphload_g2o instead")
return Graph.load_g2o(infile)


def load_g2o_r3(infile):
Expand All @@ -120,8 +65,8 @@ def load_g2o_r3(infile):
The loaded graph
"""
_LOGGER.warning("load_g2o_r3 is deprecated; use load_g2o instead")
return load_g2o(infile)
_LOGGER.warning("load_g2o_r3 is deprecated; use Graph.load_g2o instead")
return Graph.load_g2o(infile)


def load_g2o_se2(infile):
Expand All @@ -138,8 +83,8 @@ def load_g2o_se2(infile):
The loaded graph
"""
_LOGGER.warning("load_g2o_se2 is deprecated; use load_g2o instead")
return load_g2o(infile)
_LOGGER.warning("load_g2o_se2 is deprecated; use Graph.load_g2o instead")
return Graph.load_g2o(infile)


def load_g2o_se3(infile):
Expand All @@ -156,5 +101,5 @@ def load_g2o_se3(infile):
The loaded graph
"""
_LOGGER.warning("load_g2o_se3 is deprecated; use load_g2o instead")
return load_g2o(infile)
_LOGGER.warning("load_g2o_se3 is deprecated; use Graph.load_g2o instead")
return Graph.load_g2o(infile)
3 changes: 2 additions & 1 deletion tests/edge_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class BaseEdgeForTests(BaseEdge):
"""A base edge class for tests."""

def to_g2o(self):
"""Not supported, so don't do anything."""
"""Not supported, so return ``None``."""
return None

@classmethod
def from_g2o(cls, line):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_custom_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ class DistanceEdgeNumericalJacobians(BaseEdge):
"""

def to_g2o(self):
"""Not supported, so don't do anything."""
"""Not supported, so return ``None``."""
return None

@classmethod
def from_g2o(cls, line):
Expand Down
16 changes: 8 additions & 8 deletions tests/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_load_g2o_r2(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o_r2("test.g2o")
self.assertAlmostEqual(chi2, g2.calc_chi2())

Expand All @@ -99,7 +99,7 @@ def test_load_g2o_r3(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o_r3("test.g2o")
self.assertAlmostEqual(chi2, g2.calc_chi2())

Expand All @@ -114,7 +114,7 @@ def test_load_g2o_se2(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o_se2("test.g2o")
self.assertAlmostEqual(chi2, g2.calc_chi2())

Expand All @@ -129,7 +129,7 @@ def test_load_g2o_se3(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o_se3("test.g2o")
self.assertAlmostEqual(chi2, g2.calc_chi2())

Expand All @@ -145,7 +145,7 @@ def test_load_custom_edge_without_to_g2o_without_from_g2o(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o("test.g2o")
self.assertFalse(g.equals(g2))

Expand All @@ -161,7 +161,7 @@ def test_load_custom_edge_with_to_g2o_without_from_g2o(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o("test.g2o", [EdgeWithToG2OWithoutFromG2O])
self.assertFalse(g.equals(g2))

Expand All @@ -177,7 +177,7 @@ def test_load_custom_edge_without_to_g2o_with_from_g2o(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o("test.g2o", [EdgeWithoutToG2OWithFromG2O])
self.assertFalse(g.equals(g2))

Expand All @@ -193,6 +193,6 @@ def test_load_custom_edge_with_to_g2o_with_from_g2o(self):
with mock.patch("graphslam.graph.open", open_fake_file):
g.to_g2o("test.g2o")

with mock.patch("graphslam.load.open", open_fake_file):
with mock.patch("graphslam.graph.open", open_fake_file):
g2 = load_g2o("test.g2o", [EdgeWithToG2OWithFromG2O])
self.assertTrue(g.equals(g2))

0 comments on commit 391b4f3

Please sign in to comment.