Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/icalendar/prop/geo.py: 71%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""GEO property values from :rfc:`5545`."""
3from __future__ import annotations
5import math
6from typing import TYPE_CHECKING, Any, ClassVar
7from xml.etree.ElementTree import Element, SubElement
9from icalendar.error import JCalParsingError
10from icalendar.parser import Parameters
11from icalendar.parser.xcal.string import xsd_float_to_string
12from icalendar.parser.xcal.wrapper import from_xcal_wrapper
14if TYPE_CHECKING:
15 from icalendar.compatibility import Self
16 from icalendar.parser.xcal.base import XCalParser
19class vGeo:
20 """Geographic Position
22 Property Name:
23 GEO
25 Purpose:
26 This property specifies information related to the global
27 position for the activity specified by a calendar component.
29 Value Type:
30 FLOAT. The value MUST be two SEMICOLON-separated FLOAT values.
32 Property Parameters:
33 IANA and non-standard property parameters can be specified on
34 this property.
36 Conformance:
37 This property can be specified in "VEVENT" or "VTODO"
38 calendar components.
40 Description:
41 This property value specifies latitude and longitude,
42 in that order (i.e., "LAT LON" ordering). The longitude
43 represents the location east or west of the prime meridian as a
44 positive or negative real number, respectively. The longitude and
45 latitude values MAY be specified up to six decimal places, which
46 will allow for accuracy to within one meter of geographical
47 position. Receiving applications MUST accept values of this
48 precision and MAY truncate values of greater precision.
50 Example:
52 .. code-block:: ics
54 GEO:37.386013;-122.082932
56 Parse vGeo:
58 .. code-block:: pycon
60 >>> from icalendar.prop import vGeo
61 >>> geo = vGeo.from_ical('37.386013;-122.082932')
62 >>> geo
63 (37.386013, -122.082932)
65 Add a geo location to an event:
67 .. code-block:: pycon
69 >>> from icalendar import Event
70 >>> event = Event()
71 >>> latitude = 37.386013
72 >>> longitude = -122.082932
73 >>> event.add('GEO', (latitude, longitude))
74 >>> event['GEO']
75 vGeo((37.386013, -122.082932))
76 """
78 default_value: ClassVar[str] = "FLOAT"
79 params: Parameters
81 latitude: float
82 """The latitude of the geo position."""
84 longitude: float
85 """The longitude of the geo position."""
87 def __init__(
88 self,
89 geo: tuple[float | str | int, float | str | int],
90 /,
91 params: dict[str, Any] | None = None,
92 ) -> None:
93 """Create a new vGeo from a tuple of (latitude, longitude).
95 Raises:
96 ValueError: if geo is not a tuple of (latitude, longitude)
97 """
98 try:
99 latitude, longitude = (geo[0], geo[1])
100 latitude = float(latitude)
101 longitude = float(longitude)
102 except Exception as e:
103 raise ValueError(
104 "Input must be (float, float) for latitude and longitude"
105 ) from e
106 self.latitude = latitude
107 self.longitude = longitude
108 self.params = Parameters(params)
110 def to_ical(self) -> str:
111 return f"{self.latitude};{self.longitude}"
113 @property
114 def ical_value(self) -> tuple[float, float]:
115 """Geographic position as a tuple of (latitude, longitude) according to :rfc:`5545#section-3.8.1.6`."""
116 return (self.latitude, self.longitude)
118 @staticmethod
119 def from_ical(ical: str) -> tuple[float, float]:
120 try:
121 latitude, longitude = ical.split(";")
122 latitude, longitude = float(latitude), float(longitude)
123 except Exception as e:
124 raise ValueError(f"Expected 'float;float' , got: {ical}") from e
125 if not (math.isfinite(latitude) and math.isfinite(longitude)):
126 raise ValueError(f"Expected finite 'float;float', got: {ical}")
127 return (latitude, longitude)
129 def __eq__(self, other: object) -> bool:
130 return isinstance(other, vGeo) and self.to_ical() == other.to_ical()
132 def __hash__(self) -> int:
133 """Hash of the vGeo object."""
134 return hash((self.latitude, self.longitude))
136 def __repr__(self) -> str:
137 """repr(self)"""
138 return f"{self.__class__.__name__}(({self.latitude}, {self.longitude}))"
140 def to_jcal(self, name: str) -> list:
141 """Convert to jCal object."""
142 return [
143 name,
144 self.params.to_jcal(),
145 self.VALUE.lower(),
146 [self.latitude, self.longitude],
147 ]
149 @classmethod
150 def examples(cls) -> list[Self]:
151 """Examples of vGeo."""
152 return [cls((37.386013, -122.082932))]
154 from icalendar.param import VALUE
156 @classmethod
157 def from_jcal(cls, jcal_property: list) -> Self:
158 """Parse jCal from :rfc:`7265`.
160 Parameters:
161 jcal_property: The jCal property to parse.
163 Raises:
164 ~error.JCalParsingError: If the provided jCal is invalid.
165 """
166 JCalParsingError.validate_property(jcal_property, cls)
167 return cls(
168 jcal_property[3],
169 Parameters.from_jcal_property(jcal_property),
170 )
172 def to_xcal(self, element: Element) -> None:
173 """Add the xCal representation of this property according to :rfc:`6321`."""
174 geo = SubElement(element, "geo")
175 latitude = SubElement(geo, "latitude")
176 latitude.text = xsd_float_to_string(self.latitude)
177 longitude = SubElement(geo, "longitude")
178 longitude.text = xsd_float_to_string(self.longitude)
179 self.params.to_xcal(element)
181 @classmethod
182 @from_xcal_wrapper
183 def from_xcal(cls, parser: XCalParser, params: Parameters) -> Self:
184 """Parse xCal from :rfc:`6321`.
186 Parameters:
187 element: The xCal element to parse.
189 Raises:
190 ~error.XCalParsingError: If the provided xCal is invalid.
191 """
192 geo = parser.parse_tag("geo")
193 latitude = geo.parse_tag("latitude").get_xsd_float()
194 longitude = geo.parse_tag("longitude").get_xsd_float()
195 return cls((latitude, longitude), params=params)
198__all__ = ["vGeo"]