def triangulate(self): """ Convert mesh points to vectors in Cartesian space. :returns: Tuple of four elements, each being 2d numpy array of 3d vectors (the same structure and shape as the mesh itself). Those arrays are: #. points vectors, #. vectors directed from each point (excluding the last column) to the next one in a same row →, #. vectors directed from each point (excluding the first row) to the previous one in a same column ↑, #. vectors pointing from a bottom left point of each mesh cell to top right one ↗. So the last three arrays of vectors allow to construct triangles covering the whole mesh. """ points = geo_utils.spherical_to_cartesian(self.lons, self.lats, self.depths) # triangulate the mesh by defining vectors of triangles edges: # → along_azimuth = points[:, 1:] - points[:, :-1] # ↑ updip = points[:-1] - points[1:] # ↗ diag = points[:-1, 1:] - points[1:, :-1] return points, along_azimuth, updip, diag
class SphericalToCartesianAndBackTestCase(unittest.TestCase): def _test(self, (lons, lats, depths), vectors): res_cart = utils.spherical_to_cartesian(lons, lats, depths) self.assertIsInstance(res_cart, numpy.ndarray) self.assertTrue(numpy.allclose(vectors, res_cart), str(res_cart)) res_sphe = utils.cartesian_to_spherical(res_cart) self.assertIsInstance(res_sphe, tuple) self.assertEqual(len(res_sphe), 3) if depths is None: depths = numpy.zeros_like(lons) self.assertEqual( numpy.array(res_sphe).shape, numpy.array([lons, lats, depths]).shape) self.assertTrue(numpy.allclose([lons, lats, depths], res_sphe), str(res_sphe))
def _init_plane(self): """ Prepare everything needed for projecting arbitrary points on a plane containing the surface. """ tl, tr, bl, br = geo_utils.spherical_to_cartesian( self.corner_lons, self.corner_lats, self.corner_depths ) # these two parameters define the plane that contains the surface # (in 3d Cartesian space): a normal unit vector, self.normal = geo_utils.normalized(numpy.cross(tl - tr, tl - bl)) # ... and scalar "d" parameter from the plane equation (uses # an equation (3) from http://mathworld.wolfram.com/Plane.html) self.d = - (self.normal * tl).sum() # these two 3d vectors together with a zero point represent surface's # coordinate space (the way to translate 3d Cartesian space with # a center in earth's center to 2d space centered in surface's top # left corner with basis vectors directed to top right and bottom left # corners. see :meth:`_project`. self.uv1 = geo_utils.normalized(tr - tl) self.uv2 = numpy.cross(self.normal, self.uv1) self.zero_zero = tl
def _project(self, lons, lats, depths): """ Project points to a surface's plane. Parameters are lists or numpy arrays of coordinates of points to project. :returns: A tuple of three items: distances between original points and surface's plane in km, "x" and "y" coordinates of points' projections to the plane (in a surface's coordinate space). """ points = geo_utils.spherical_to_cartesian(lons, lats, depths) # uses method from http://www.9math.com/book/projection-point-plane dists = (self.normal * points).sum(axis=-1) + self.d t0 = - dists projs = points + self.normal * t0.reshape(t0.shape + (1, )) # translate projected points' to surface's coordinate space vectors2d = projs - self.zero_zero xx = (vectors2d * self.uv1).sum(axis=-1) yy = (vectors2d * self.uv2).sum(axis=-1) return dists, xx, yy
def test_from_vector(self): point = geo.Point(12.34, -56.78, 91.011) vector = spherical_to_cartesian(point.longitude, point.latitude, point.depth) self.assertEqual(point, geo.Point.from_vector(vector))