-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathpoint.py
211 lines (188 loc) · 6.16 KB
/
point.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
'''
A class for points used in the GIS Algorithms book.
Change history
October 3, 2024
Now the class is iterable
Change representation to [x,y]
September 28, 2024
f-strings are used now.
Type checking for __init__.
Convert a number in string when creating a Point object.
Error handling.
December 10, 2022
Now use *args and **kwargs in __init__ to make it more flexible
September 30, 2017
Change __repr__ to return a new string like 'Point(x,y)'
April 24, 2017
Remove long from isinstance. Python 3 no longer support long
March 1, 2017
More updates on __str__ to make sure integers are printed correctly.
October 28, 2015
Functions __repr__ and __str__ are updated to be more flexible and robust.
November 10, 2015
Add a key member to the class
Contact:
Ningchuan Xiao
The Ohio State University
Columbus, OH
'''
__author__ = 'Ningchuan Xiao <ncxiao@gmail.com>'
from math import sqrt
class Point:
'''
A class for points in Cartesian coordinate systems.
Examples:
A Point object at (10, 1) with an attribute of 100 can be created using the following:
Point(10, 1, 100)
Point(x=10, y=1, key=100)
These are also valid examples:
Point(10)
Point(10, 1)
Point(x=10, y=1)
Point(1, y=12)
Point(1, key='any attributes')
Point(1, 2, 'any attributes')
Point(1, 2, key='any attributes')
Point(1, '12')
Point('1', 12)
Point([1, 12])
An object of this class is iterable:
p = Point(10, 11)
for i in p:
print(i)
'''
# def __init__(self, x=None, y=None, key=None):
# self.x = x
# self.y = y
# self.key = key
def __init__(self, *args, **kwargs): # x=None, y=None, key=None):
'''
Both x and y can be None. If not, they need to be numerical.
Key is the attributes and can be of any type.
An exception will be raised if a non-numerical value is used in either x or y.
'''
self.x = None
self.y = None
self.key = None
self.idx = 0
if len(args) == 1:
if isinstance(args[0], (list, tuple)):
if len(args[0]) == 1:
self.x = args[0][0]
if len(args[0]) == 2:
self.x = args[0][0]
self.y = args[0][1]
else:
self.x = args[0]
if 'y' in kwargs.keys():
self.y = kwargs['y']
if 'key' in kwargs.keys():
self.key = kwargs['key']
elif len(args) == 2:
self.x = args[0]
self.y = args[1]
self.key = None
if 'key' in kwargs.keys():
self.key = kwargs['key']
elif len(args) == 3:
self.x = args[0]
self.y = args[1]
self.key = args[2]
else:
if 'x' in kwargs.keys():
self.x = kwargs['x']
if 'y' in kwargs.keys():
self.y = kwargs['y']
if 'key' in kwargs.keys():
self.key = kwargs['key']
if self.x and not isinstance(self.x, (int, float)):
try:
self.x = float(self.x)
except Exception as e:
print(e)
raise Exception('XCoordinateTypeError') from None # don't traceback
if self.y and not isinstance(self.y, (int, float)):
try:
self.y = float(self.y)
except Exception as e:
print(e)
raise Exception('YCoordinateTypeError') from None # don't traceback
def __getitem__(self, i):
if i==0:
return self.x
if i==1:
return self.y
return None
def __len__(self):
return 2
def __eq__(self, other):
if isinstance(other, Point):
return self.x==other.x and self.y==other.y
return NotImplemented
def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result
def __lt__(self, other):
if isinstance(other, Point):
if self.x<other.x:
return True
elif self.x==other.x and self.y<other.y:
return True
return False
return NotImplemented
def __gt__(self, other):
if isinstance(other, Point):
if self.x>other.x:
return True
elif self.x==other.x and self.y>other.y:
return True
return False
return NotImplemented
def __ge__(self, other):
if isinstance(other, Point):
if self > other or self == other:
return True
return False
return NotImplemented
def __le__(self, other):
if isinstance(other, Point):
if self < other or self == other:
return True
return False
return NotImplemented
def isvalid(self):
if not isinstance(self.x, (int, float)) \
or not isinstance(self.y, (int, float)):
return False
return True
def __str__(self):
'''NaP: Not a point'''
if not self.isvalid():
return 'NaP'
fmtstr = f'({self.x}' if isinstance(self.x, int) else f'({self.x:.1f}'
fmtstr += f', {self.y})' if isinstance(self.y, int) else f', {self.y:.1f})'
# if isinstance(self.x, (int)):
# fmtstr = f'({self.x}, '
# else:
# fmtstr = f'({self.x:.1f}, '
# if isinstance(self.y, (int)):
# fmtstr += f'{self.y})'
# else:
# fmtstr += f'{self.y:.1f})'
return fmtstr
def __repr__(self):
# return f'Point({self.x}, {self.y})'
return f'[{self.x}, {self.y}]'
def distance(self, other):
return sqrt((self.x-other.x)**2 + (self.y-other.y)**2)
def __iter__(self):
return self
def __next__(self):
if self.idx >= len(self):
self.idx = 0
raise StopIteration
else:
self.idx += 1
return self[self.idx-1]