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

75 statements  

1"""GEO property values from :rfc:`5545`.""" 

2 

3from __future__ import annotations 

4 

5import math 

6from typing import TYPE_CHECKING, Any, ClassVar 

7from xml.etree.ElementTree import Element, SubElement 

8 

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 

13 

14if TYPE_CHECKING: 

15 from icalendar.compatibility import Self 

16 from icalendar.parser.xcal.base import XCalParser 

17 

18 

19class vGeo: 

20 """Geographic Position 

21 

22 Property Name: 

23 GEO 

24 

25 Purpose: 

26 This property specifies information related to the global 

27 position for the activity specified by a calendar component. 

28 

29 Value Type: 

30 FLOAT. The value MUST be two SEMICOLON-separated FLOAT values. 

31 

32 Property Parameters: 

33 IANA and non-standard property parameters can be specified on 

34 this property. 

35 

36 Conformance: 

37 This property can be specified in "VEVENT" or "VTODO" 

38 calendar components. 

39 

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. 

49 

50 Example: 

51 

52 .. code-block:: ics 

53 

54 GEO:37.386013;-122.082932 

55 

56 Parse vGeo: 

57 

58 .. code-block:: pycon 

59 

60 >>> from icalendar.prop import vGeo 

61 >>> geo = vGeo.from_ical('37.386013;-122.082932') 

62 >>> geo 

63 (37.386013, -122.082932) 

64 

65 Add a geo location to an event: 

66 

67 .. code-block:: pycon 

68 

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 """ 

77 

78 default_value: ClassVar[str] = "FLOAT" 

79 params: Parameters 

80 

81 latitude: float 

82 """The latitude of the geo position.""" 

83 

84 longitude: float 

85 """The longitude of the geo position.""" 

86 

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). 

94 

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) 

109 

110 def to_ical(self) -> str: 

111 return f"{self.latitude};{self.longitude}" 

112 

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) 

117 

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) 

128 

129 def __eq__(self, other: object) -> bool: 

130 return isinstance(other, vGeo) and self.to_ical() == other.to_ical() 

131 

132 def __hash__(self) -> int: 

133 """Hash of the vGeo object.""" 

134 return hash((self.latitude, self.longitude)) 

135 

136 def __repr__(self) -> str: 

137 """repr(self)""" 

138 return f"{self.__class__.__name__}(({self.latitude}, {self.longitude}))" 

139 

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 ] 

148 

149 @classmethod 

150 def examples(cls) -> list[Self]: 

151 """Examples of vGeo.""" 

152 return [cls((37.386013, -122.082932))] 

153 

154 from icalendar.param import VALUE 

155 

156 @classmethod 

157 def from_jcal(cls, jcal_property: list) -> Self: 

158 """Parse jCal from :rfc:`7265`. 

159 

160 Parameters: 

161 jcal_property: The jCal property to parse. 

162 

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 ) 

171 

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) 

180 

181 @classmethod 

182 @from_xcal_wrapper 

183 def from_xcal(cls, parser: XCalParser, params: Parameters) -> Self: 

184 """Parse xCal from :rfc:`6321`. 

185 

186 Parameters: 

187 element: The xCal element to parse. 

188 

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) 

196 

197 

198__all__ = ["vGeo"]