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

212

213

214

215

import numpy as num 

import math 

 

from pyrocko import orthodrome 

from pyrocko.guts import Object, Float 

 

 

guts_prefix = 'pf' 

 

d2r = math.pi / 180. 

r2d = 1.0 / d2r 

km = 1000. 

 

 

def latlondepth_to_cartesian(lat, lon, depth): 

return orthodrome.geodetic_to_ecef(lat, lon, -depth) 

 

 

class Location(Object): 

''' 

Geographical location. 

 

The location is given by a reference point at the earth's surface 

(:py:attr:`lat`, :py:attr:`lon`, :py:attr:`elevation`) and a cartesian 

offset from this point (:py:attr:`north_shift`, :py:attr:`east_shift`, 

:py:attr:`depth`). The offset corrected lat/lon coordinates of the location 

can be accessed though the :py:attr:`effective_latlon`, 

:py:attr:`effective_lat`, and :py:attr:`effective_lon` properties. 

''' 

 

lat = Float.T( 

default=0.0, 

optional=True, 

help='latitude of reference point [deg]') 

 

lon = Float.T( 

default=0.0, 

optional=True, 

help='longitude of reference point [deg]') 

 

north_shift = Float.T( 

default=0., 

optional=True, 

help='northward cartesian offset from reference point [m]') 

 

east_shift = Float.T( 

default=0., 

optional=True, 

help='eastward cartesian offset from reference point [m]') 

 

elevation = Float.T( 

default=0.0, 

optional=True, 

help='surface elevation, above sea level [m]') 

 

depth = Float.T( 

default=0.0, 

help='depth, below surface [m]') 

 

def __init__(self, **kwargs): 

Object.__init__(self, **kwargs) 

self._latlon = None 

 

def __setattr__(self, name, value): 

if name in ('lat', 'lon', 'north_shift', 'east_shift'): 

self.__dict__['_latlon'] = None 

 

Object.__setattr__(self, name, value) 

 

@property 

def effective_latlon(self): 

''' 

Property holding the offset-corrected lat/lon pair of the location. 

''' 

 

if self._latlon is None: 

if self.north_shift == 0.0 and self.east_shift == 0.0: 

self._latlon = self.lat, self.lon 

else: 

self._latlon = tuple(float(x) for x in orthodrome.ne_to_latlon( 

self.lat, self.lon, self.north_shift, self.east_shift)) 

 

return self._latlon 

 

@property 

def effective_lat(self): 

''' 

Property holding the offset-corrected latitude of the location. 

''' 

 

return self.effective_latlon[0] 

 

@property 

def effective_lon(self): 

''' 

Property holding the offset-corrected longitude of the location. 

''' 

 

return self.effective_latlon[1] 

 

def same_origin(self, other): 

''' 

Check whether other location object has the same reference location. 

''' 

 

return self.lat == other.lat and self.lon == other.lon 

 

def distance_to(self, other): 

''' 

Compute surface distance [m] to other location object. 

 

 

''' 

 

if self.same_origin(other): 

other_north_shift, other_east_shift = get_offset(other) 

 

return math.sqrt((self.north_shift - other_north_shift)**2 + 

(self.east_shift - other_east_shift)**2) 

 

else: 

slat, slon = self.effective_latlon 

rlat, rlon = get_effective_latlon(other) 

 

return float(orthodrome.distance_accurate50m_numpy( 

slat, slon, rlat, rlon)[0]) 

 

def distance_3d_to(self, other): 

''' 

Compute 3D distance [m] to other location object. 

 

All coordinates are transformed to cartesian coordinates if necessary 

then distance is: 

 

.. math:: 

 

\\Delta = \\sqrt{\\Delta {\\bf x}^2 + \\Delta {\\bf y}^2 + \ 

\\Delta {\\bf z}^2} 

 

''' 

 

if self.same_origin(other): 

other_north_shift, other_east_shift = get_offset(other) 

return math.sqrt((self.north_shift - other_north_shift)**2 + 

(self.east_shift - other_east_shift)**2 + 

(self.depth - other.depth)**2) 

 

else: 

slat, slon = self.effective_latlon 

rlat, rlon = get_effective_latlon(other) 

 

sx, sy, sz = latlondepth_to_cartesian(slat, slon, self.depth) 

rx, ry, rz = latlondepth_to_cartesian(rlat, rlon, other.depth) 

 

return math.sqrt((sx-rx)**2 + (sy-ry)**2 + (sz-rz)**2) 

 

def offset_to(self, other): 

if self.same_origin(other): 

other_north_shift, other_east_shift = get_offset(other) 

return ( 

other_north_shift - self.north_shift, 

other_east_shift - self.east_shift) 

 

else: 

azi, bazi = self.azibazi_to(other) 

dist = self.distance_to(other) 

return dist*math.cos(azi*d2r), dist*math.sin(azi*d2r) 

 

def azibazi_to(self, other): 

''' 

Compute azimuth and backazimuth to and from other location object. 

''' 

 

if self.same_origin(other): 

other_north_shift, other_east_shift = get_offset(other) 

azi = r2d * math.atan2(other_east_shift - self.east_shift, 

other_north_shift - self.north_shift) 

 

bazi = azi + 180. 

else: 

slat, slon = self.effective_latlon 

rlat, rlon = get_effective_latlon(other) 

azi, bazi = orthodrome.azibazi_numpy(slat, slon, rlat, rlon) 

 

return float(azi), float(bazi) 

 

def set_origin(self, lat, lon): 

lat = float(lat) 

lon = float(lon) 

elat, elon = self.effective_latlon 

n, e = orthodrome.latlon_to_ne_numpy(lat, lon, elat, elon) 

self.lat = lat 

self.lon = lon 

self.north_shift = float(n) 

self.east_shift = float(e) 

self._latlon = elat, elon # unchanged 

 

@property 

def coords5(self): 

return num.array([ 

self.lat, self.lon, self.north_shift, self.east_shift, self.depth]) 

 

 

def get_offset(obj): 

try: 

return obj.north_shift, obj.east_shift 

except AttributeError: 

return 0.0, 0.0 

 

 

def get_effective_latlon(obj): 

try: 

return obj.effective_latlon 

except AttributeError: 

return obj.lat, obj.lon