tkrajina/gpxpy

AttributeError: 'NoneType' object has no attribute 'offset' in SimpleTZ

reteptilian opened this issue · 2 comments

I ran into this error trying to plot points with matplotlib (see code below to repro).

I worked around it by modifyingSimpleTZ.__eq__ to do an isinstance check:

    def __eq__(self, other: Any) -> bool:
        if not isinstance(other, SimpleTZ):
            return NotImplemented
        return self.offset == other.offset # type: ignore

Here is code to repro. This is with python 3.8.3, gpxpy 1.4.2 and matplotlib 3.2.2.

import gpxpy
import matplotlib.pyplot as plt

gpx_file = """<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.0" creator="GPSBabel - http://www.gpsbabel.org"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.topografix.com/GPX/1/0" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
  <time>2010-08-06T10:36:35Z</time>
  <bounds minlat="45.735199945" minlon="14.288633270" maxlat="45.795349991" maxlon="14.377516648"/>
  <trk>
    <name>ACTIVE LOG #2</name>
    <number>1</number>
    <trkseg>
      <trkpt lat="45.772175035" lon="14.357659249">
        <ele>542.320923</ele>
        <time>2010-08-05T14:23:59Z</time>
      </trkpt>
    </trkseg>
  </trk>
</gpx>
"""
gpx = gpxpy.parse(gpx_file)

point = gpx.tracks[0].segments[0].points[0]
plt.plot(point.time, point.longitude)

Stack trace:

Traceback (most recent call last):
  File "c:/Users/pkesb/test.py", line 25, in <module>
    plt.plot(point.time, point.longitude)
  File "C:\Users\pkesb\anaconda3\lib\site-packages\matplotlib\pyplot.py", line 2761, in plot
    return gca().plot(
  File "C:\Users\pkesb\anaconda3\lib\site-packages\matplotlib\axes\_axes.py", line 1647, in plot
    lines = [*self._get_lines(*args, data=data, **kwargs)]
  File "C:\Users\pkesb\anaconda3\lib\site-packages\matplotlib\axes\_base.py", line 216, in __call__
    yield from self._plot_args(this, kwargs)
  File "C:\Users\pkesb\anaconda3\lib\site-packages\matplotlib\axes\_base.py", line 337, in _plot_args
    self.axes.xaxis.update_units(x)
  File "C:\Users\pkesb\anaconda3\lib\site-packages\matplotlib\axis.py", line 1518, in update_units
    self.set_units(default)
  File "C:\Users\pkesb\anaconda3\lib\site-packages\matplotlib\axis.py", line 1587, in set_units
    if u == self.units:
  File "C:\Users\pkesb\anaconda3\lib\site-packages\gpxpy\gpxfield.py", line 75, in __eq__
    return self.offset == other.offset # type: ignore
AttributeError: 'NoneType' object has no attribute 'offset'

It seems to me, as a newcomer in gpxpy, there are problems related to SimpleTZ (such as issue #190). I am not sure what is the best way: mod of SimpleTZ to a fully functional one, use of built-in zoneinfo in python 3.9+ (which is backported to 3.6+), keep SimpleTZ as simple as possible, etc.

Another workaround without the mod of gpxpy is to replace tzinfo.

import gpxpy
import matplotlib.pyplot as plt
import datetime as mod_datetime

def replace_tzinfo(dt):
    return dt.replace(tzinfo=mod_datetime.timezone(dt.utcoffset()))

gpx = gpxpy.parse(gpx_file)

for track in gpx.tracks:
    for segment in track.segments:
        for point in segment.points:
            point.time = replace_tzinfo(point.time)

point = gpx.tracks[0].segments[0].points[0]
plt.plot(point.time, point.longitude)