Esempio n. 1
0
    def distance_to_point(self, lon, lat):
        """Find the shortest geodesic distance from (lon, lat) to a nonzero
           cell of the probability matrix."""
        aeqd_pt = pyproj.Proj(proj='aeqd',
                              ellps='WGS84',
                              datum='WGS84',
                              lon_0=lon,
                              lat_0=lat)
        wgs_to_aeqd = functools.partial(pyproj.transform, wgs_proj, aeqd_pt)
        # mathematically, wgs_to_aeqd(Point(lon, lat)) == Point(0, 0);
        # the latter is faster and more precise
        pt = Point(0, 0)

        # It is unacceptably costly to construct a shapely MultiPoint
        # out of some locations with large regions (requires more than
        # 32GB of scratch memory).  Instead, iterate over the points
        # one at a time.
        min_distance = math.inf
        for x, y, v in iter_csr_nonzero(self.probability):
            cell = sh_transform(wgs_to_aeqd,
                                Point(self.longitudes[x], self.latitudes[y]))
            if pt.distance(cell) - self.resolution * 2 < min_distance:
                cell = cell.buffer(self.resolution * 3 / 2)
                min_distance = min(min_distance, pt.distance(cell))

        if min_distance < self.resolution * 3 / 2:
            return 0
        return min_distance
Esempio n. 2
0
    def area(self):
        """Weighted area of the nonzero region of the probability matrix."""

        if self._area is None:
            # Notionally, each grid point should be treated as a
            # rectangle of parallels and meridians _centered_ on the
            # point.  The area of such a rectangle, however, only
            # depends on its latitude and its breadth; the actual
            # longitude values don't matter.  Since the grid is
            # equally spaced, we can use [0],[1] always, and then we
            # do not have to worry about crossing the discontinuity at ±180.
            west = self.longitudes[0]
            east = self.longitudes[1]
            # For latitude, the actual values do matter, but the map
            # never goes all the way to the poles, and the grid is
            # equally spaced, so we can precompute the north-south
            # delta from any pair of latitudes and not have to worry
            # about running off the ends of the array.
            d_lat = (self.latitudes[1] - self.latitudes[0]) / 2

            # We don't need X, so throw it away immediately.  (We
            # don't use iter_csr_nonzero here because we need to
            # modify V.)
            X, Y, V = sparse.find(self.probability)
            X = None

            # The value vector is supposed to be normalized, but make
            # sure it is, and then adjust from 1-overall to 1-per-cell
            # normalization.
            assert len(V.shape) == 1
            S = V.sum()
            if S == 0:
                return 0
            if S != 1:
                V /= S
            V *= V.shape[0]

            area = 0
            for y, v in zip(Y, V):
                north = self.latitudes[y] + d_lat
                south = self.latitudes[y] - d_lat
                if not (-90 <= south < north <= 90):
                    raise AssertionError(
                        "expected -90 <= {} < {} <= 90".format(south, north))

                tile = sh_transform(wgs_to_cea, Box(west, south, east, north))
                area += v * tile.area

            self._area = area
        return self._area
Esempio n. 3
0
def coord_transform_geometry(geo: (BaseGeometry, BaseMultipartGeometry),
                             from_srs: SRS, to_srs: SRS):
    """对Geomery内的所有点进行坐标转换,返回转换后的Geometry

    该方法可以支持所有的Shapely Geometry形状,包括Point, Line, Polygon,
    MultiPloygon等,返回的Geometry和输入的形状保持一致
    Args:
      geo: 输入的shapely Geometry
      from_srs: 输入的坐标格式
      to_srs: 输出的坐标格式
    Returns:
      转换后的shapely Geometry
    """
    return sh_transform(
        lambda x, y, z=None: coord_transform(x, y, from_srs, to_srs), geo)
Esempio n. 4
0
def DiskOnGlobe(x, y, radius):
    """Return a shapely polygon which is a circle centered at longitude X,
       latitude Y, with radius RADIUS (meters), projected onto the
       surface of the Earth.
    """

    # Make sure the radius is at least 1km to prevent underflow.
    radius = max(radius, 1000)

    # If the radius is too close to half the circumference of the
    # Earth, the projection operation below will produce an invalid
    # polygon.  We don't get much use out of a region that includes
    # the whole planet but for a tiny disk (which will probably be
    # somewhere in the ocean anyway) so just give up and say that the
    # region is the entire planet.
    if radius > 19975000:
        return Box(-180, -90, 180, 90)

    # To find all points on the Earth within a certain distance of
    # a reference latitude and longitude, back-project onto the
    # Earth from an azimuthal-equidistant map with its zero point
    # at the reference latitude and longitude.
    aeqd = pyproj.Proj(proj='aeqd', ellps='WGS84', datum='WGS84', lat_0=y, lon_0=x)

    disk = sh_transform(
        functools.partial(pyproj.transform, aeqd, wgs_proj),
        Point(0, 0).buffer(radius, resolution=64))

    # Two special cases must be manually dealt with.  First, if any
    # side of the "disk" (really a many-sided polygon) crosses the
    # coordinate singularity at longitude ±180, we must replace that
    # side with a diversion to either the north or south pole
    # (whichever is closer) to ensure the diskstill encloses all of
    # the area it should.
    boundary = np.array(disk.boundary)
    i = 0
    while i < boundary.shape[0] - 1:
        if abs(boundary[i+1,0] - boundary[i,0]) > 180:
            pole = -90 if boundary[i,1] < 0 else 90
            west = -180 if boundary[i,0] < 0 else 180
            east = 180 if boundary[i,0] < 0 else -180

            boundary = np.insert(boundary, i+1, [
                [west, boundary[i,1]],
                [west, pole],
                [east, pole],
                [east, boundary[i+1,1]]
            ], axis=0)
            i += 5
        else:
            i += 1
    # If there were two sides that crossed the singularity and they
    # were both on the same side of the equator, the excursions will
    # coincide and shapely will be unhappy.  buffer(0) corrects this.
    disk = Polygon(boundary).buffer(0)

    # Second, if the radius is very large, the projected disk might
    # enclose the complement of the region that it ought to enclose.
    # If it doesn't contain the reference point, we must subtract it
    # from the entire map.
    origin = Point(x, y)
    if not disk.contains(origin):
        disk = Box(-180, -90, 180, 90).difference(disk)

    assert disk.is_valid
    assert disk.contains(origin)

    return disk
Esempio n. 5
0
    def compute_bounding_region_now(self):
        if self._bounds is not None: return

        distance_bound = self.range_fn.distance_bound()

        # If the distance bound is too close to half the circumference
        # of the Earth, the projection operation below will produce an
        # invalid polygon.  We don't get much use out of a bounding
        # region that includes the whole planet but for a tiny disk
        # (which will probably be somewhere in the ocean anyway) so
        # just give up and say that the bound is the entire planet.
        # Similarly, if the distance bound is zero, give up.
        if distance_bound > 19975000 or distance_bound == 0:
            self._bounds = Box(self.west, self.south, self.east, self.north)
            return

        # To find all points on the Earth within a certain distance of
        # a reference latitude and longitude, back-project onto the
        # Earth from an azimuthal-equidistant map with its zero point
        # at the reference latitude and longitude.
        aeqd = pyproj.Proj(proj='aeqd',
                           ellps='WGS84',
                           datum='WGS84',
                           lat_0=self.ref_lat,
                           lon_0=self.ref_lon)

        try:
            disk = sh_transform(
                functools.partial(pyproj.transform, aeqd, wgs_proj),
                Disk(0, 0, distance_bound))

            # Two special cases must be manually dealt with.  First, if
            # any side of the "circle" (really a many-sided polygon)
            # crosses the coordinate singularity at longitude ±180, we
            # must replace it with a diversion to either the north or
            # south pole (whichever is closer) to ensure that it still
            # encloses all of the area it should.
            boundary = np.array(disk.boundary)
            i = 0
            while i < boundary.shape[0] - 1:
                if abs(boundary[i + 1, 0] - boundary[i, 0]) > 180:
                    pole = self.south if boundary[i, 1] < 0 else self.north
                    west = self.west if boundary[i, 0] < 0 else self.east
                    east = self.east if boundary[i, 0] < 0 else self.west

                    boundary = np.insert(
                        boundary,
                        i + 1, [[west, boundary[i, 1]], [west, pole],
                                [east, pole], [east, boundary[i + 1, 1]]],
                        axis=0)
                    i += 5
                else:
                    i += 1
            # If there were two edges that crossed the singularity and they
            # were both on the same side of the equator, the excursions will
            # coincide and shapely will be unhappy.  buffer(0) corrects this.
            disk = Polygon(boundary).buffer(0)

            # Second, if the disk is very large, the projected disk might
            # enclose the complement of the region that it ought to enclose.
            # If it doesn't contain the reference point, we must subtract it
            # from the entire map.
            origin = Point(self.ref_lon, self.ref_lat)
            if not disk.contains(origin):
                disk = (Box(self.west, self.south, self.east,
                            self.north).difference(disk))

            assert disk.is_valid
            assert disk.contains(origin)
            self._bounds = disk
        except Exception as e:
            setattr(e, 'offending_disk', disk)
            setattr(e, 'offending_obs', self)
            raise